Merge pull request #9803 from Techjar/bbox-videocommon

VideoCommon: Abstract bounding box
This commit is contained in:
Léo Lam
2021-10-08 22:24:38 +02:00
committed by GitHub
41 changed files with 617 additions and 708 deletions

View File

@ -3,7 +3,6 @@
#include <vector>
#include "Common/Assert.h"
#include "Common/Logging/Log.h"
#include "VideoBackends/Vulkan/CommandBufferManager.h"
@ -16,11 +15,7 @@
namespace Vulkan
{
BoundingBox::BoundingBox()
{
}
BoundingBox::~BoundingBox()
VKBoundingBox::~VKBoundingBox()
{
if (m_gpu_buffer != VK_NULL_HANDLE)
{
@ -29,14 +24,8 @@ BoundingBox::~BoundingBox()
}
}
bool BoundingBox::Initialize()
bool VKBoundingBox::Initialize()
{
if (!g_ActiveConfig.backend_info.bSupportsBBox)
{
WARN_LOG_FMT(VIDEO, "Vulkan: Bounding box is unsupported by your device.");
return true;
}
if (!CreateGPUBuffer())
return false;
@ -48,103 +37,71 @@ bool BoundingBox::Initialize()
return true;
}
void BoundingBox::Flush()
std::vector<BBoxType> VKBoundingBox::Read(u32 index, u32 length)
{
if (m_gpu_buffer == VK_NULL_HANDLE)
return;
// Can't be done within a render pass.
StateTracker::GetInstance()->EndRenderPass();
// Combine updates together, chances are the game would have written all 4.
bool updated_buffer = false;
for (size_t start = 0; start < 4; start++)
{
if (!m_values_dirty[start])
continue;
// Ensure all writes are completed to the GPU buffer prior to the transfer.
StagingBuffer::BufferMemoryBarrier(
g_command_buffer_mgr->GetCurrentCommandBuffer(), m_gpu_buffer,
VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, 0,
BUFFER_SIZE, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
m_readback_buffer->PrepareForGPUWrite(g_command_buffer_mgr->GetCurrentCommandBuffer(),
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT);
size_t count = 0;
std::array<s32, 4> write_values;
for (; (start + count) < 4; count++)
{
if (!m_values_dirty[start + count])
break;
// Copy from GPU -> readback buffer.
VkBufferCopy region = {0, 0, BUFFER_SIZE};
vkCmdCopyBuffer(g_command_buffer_mgr->GetCurrentCommandBuffer(), m_gpu_buffer,
m_readback_buffer->GetBuffer(), 1, &region);
m_readback_buffer->Read((start + count) * sizeof(s32), &write_values[count], sizeof(s32),
false);
m_values_dirty[start + count] = false;
}
// Restore GPU buffer access.
StagingBuffer::BufferMemoryBarrier(
g_command_buffer_mgr->GetCurrentCommandBuffer(), m_gpu_buffer, VK_ACCESS_TRANSFER_READ_BIT,
VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, 0, BUFFER_SIZE,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
m_readback_buffer->FlushGPUCache(g_command_buffer_mgr->GetCurrentCommandBuffer(),
VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
// We can't issue vkCmdUpdateBuffer within a render pass.
// However, the writes must be serialized, so we can't put it in the init buffer.
if (!updated_buffer)
{
StateTracker::GetInstance()->EndRenderPass();
// Wait until these commands complete.
Renderer::GetInstance()->ExecuteCommandBuffer(false, true);
// Ensure GPU buffer is in a state where it can be transferred to.
StagingBuffer::BufferMemoryBarrier(
g_command_buffer_mgr->GetCurrentCommandBuffer(), m_gpu_buffer,
VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, 0,
BUFFER_SIZE, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
// Cache is now valid.
m_readback_buffer->InvalidateCPUCache();
updated_buffer = true;
}
// Read out the values and return
std::vector<BBoxType> values(length);
m_readback_buffer->Read(index * sizeof(BBoxType), values.data(), length * sizeof(BBoxType),
false);
return values;
}
vkCmdUpdateBuffer(g_command_buffer_mgr->GetCurrentCommandBuffer(), m_gpu_buffer,
start * sizeof(s32), count * sizeof(s32),
reinterpret_cast<const u32*>(write_values.data()));
}
void VKBoundingBox::Write(u32 index, const std::vector<BBoxType>& values)
{
// We can't issue vkCmdUpdateBuffer within a render pass.
// However, the writes must be serialized, so we can't put it in the init buffer.
StateTracker::GetInstance()->EndRenderPass();
// Ensure GPU buffer is in a state where it can be transferred to.
StagingBuffer::BufferMemoryBarrier(
g_command_buffer_mgr->GetCurrentCommandBuffer(), m_gpu_buffer,
VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, 0,
BUFFER_SIZE, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
// Write the values to the GPU buffer
vkCmdUpdateBuffer(g_command_buffer_mgr->GetCurrentCommandBuffer(), m_gpu_buffer,
index * sizeof(BBoxType), values.size() * sizeof(BBoxType),
reinterpret_cast<const BBoxType*>(values.data()));
// Restore fragment shader access to the buffer.
if (updated_buffer)
{
StagingBuffer::BufferMemoryBarrier(
g_command_buffer_mgr->GetCurrentCommandBuffer(), m_gpu_buffer, VK_ACCESS_TRANSFER_WRITE_BIT,
VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, 0, BUFFER_SIZE,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
}
// We're now up-to-date.
m_valid = true;
StagingBuffer::BufferMemoryBarrier(
g_command_buffer_mgr->GetCurrentCommandBuffer(), m_gpu_buffer, VK_ACCESS_TRANSFER_WRITE_BIT,
VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, 0, BUFFER_SIZE,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
}
void BoundingBox::Invalidate()
{
if (m_gpu_buffer == VK_NULL_HANDLE)
return;
m_valid = false;
}
s32 BoundingBox::Get(size_t index)
{
ASSERT(index < NUM_VALUES);
if (!m_valid)
Readback();
s32 value;
m_readback_buffer->Read(index * sizeof(s32), &value, sizeof(value), false);
return value;
}
void BoundingBox::Set(size_t index, s32 value)
{
ASSERT(index < NUM_VALUES);
// If we're currently valid, update the stored value in both our cache and the GPU buffer.
if (m_valid)
{
// Skip when it hasn't changed.
s32 current_value;
m_readback_buffer->Read(index * sizeof(s32), &current_value, sizeof(current_value), false);
if (current_value == value)
return;
}
// Flag as dirty, and update values.
m_readback_buffer->Write(index * sizeof(s32), &value, sizeof(value), true);
m_values_dirty[index] = true;
}
bool BoundingBox::CreateGPUBuffer()
bool VKBoundingBox::CreateGPUBuffer()
{
VkBufferUsageFlags buffer_usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
@ -204,7 +161,7 @@ bool BoundingBox::CreateGPUBuffer()
return true;
}
bool BoundingBox::CreateReadbackBuffer()
bool VKBoundingBox::CreateReadbackBuffer()
{
m_readback_buffer = StagingBuffer::Create(STAGING_BUFFER_TYPE_READBACK, BUFFER_SIZE,
VK_BUFFER_USAGE_TRANSFER_DST_BIT);
@ -215,39 +172,4 @@ bool BoundingBox::CreateReadbackBuffer()
return true;
}
void BoundingBox::Readback()
{
// Can't be done within a render pass.
StateTracker::GetInstance()->EndRenderPass();
// Ensure all writes are completed to the GPU buffer prior to the transfer.
StagingBuffer::BufferMemoryBarrier(
g_command_buffer_mgr->GetCurrentCommandBuffer(), m_gpu_buffer,
VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, 0,
BUFFER_SIZE, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
m_readback_buffer->PrepareForGPUWrite(g_command_buffer_mgr->GetCurrentCommandBuffer(),
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT);
// Copy from GPU -> readback buffer.
VkBufferCopy region = {0, 0, BUFFER_SIZE};
vkCmdCopyBuffer(g_command_buffer_mgr->GetCurrentCommandBuffer(), m_gpu_buffer,
m_readback_buffer->GetBuffer(), 1, &region);
// Restore GPU buffer access.
StagingBuffer::BufferMemoryBarrier(
g_command_buffer_mgr->GetCurrentCommandBuffer(), m_gpu_buffer, VK_ACCESS_TRANSFER_READ_BIT,
VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, 0, BUFFER_SIZE,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
m_readback_buffer->FlushGPUCache(g_command_buffer_mgr->GetCurrentCommandBuffer(),
VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
// Wait until these commands complete.
Renderer::GetInstance()->ExecuteCommandBuffer(false, true);
// Cache is now valid.
m_readback_buffer->InvalidateCPUCache();
m_valid = true;
}
} // namespace Vulkan

View File

@ -8,41 +8,35 @@
#include <string>
#include "Common/CommonTypes.h"
#include "VideoBackends/Vulkan/VulkanLoader.h"
#include "VideoCommon/BoundingBox.h"
namespace Vulkan
{
class StagingBuffer;
class BoundingBox
class VKBoundingBox final : public BoundingBox
{
public:
BoundingBox();
~BoundingBox();
~VKBoundingBox() override;
bool Initialize();
bool Initialize() override;
s32 Get(size_t index);
void Set(size_t index, s32 value);
void Invalidate();
void Flush();
protected:
std::vector<BBoxType> Read(u32 index, u32 length) override;
void Write(u32 index, const std::vector<BBoxType>& values) override;
private:
bool CreateGPUBuffer();
bool CreateReadbackBuffer();
void Readback();
VkBuffer m_gpu_buffer = VK_NULL_HANDLE;
VkDeviceMemory m_gpu_memory = VK_NULL_HANDLE;
static const size_t NUM_VALUES = 4;
static const size_t BUFFER_SIZE = sizeof(u32) * NUM_VALUES;
static constexpr size_t BUFFER_SIZE = sizeof(BBoxType) * NUM_BBOX_VALUES;
std::unique_ptr<StagingBuffer> m_readback_buffer;
std::array<bool, NUM_VALUES> m_values_dirty = {};
bool m_valid = true;
};
} // namespace Vulkan

View File

@ -17,6 +17,7 @@
#include "VideoBackends/Vulkan/CommandBufferManager.h"
#include "VideoBackends/Vulkan/ObjectCache.h"
#include "VideoBackends/Vulkan/StagingBuffer.h"
#include "VideoBackends/Vulkan/StateTracker.h"
#include "VideoBackends/Vulkan/VKBoundingBox.h"
#include "VideoBackends/Vulkan/VKPerfQuery.h"
@ -63,13 +64,6 @@ bool Renderer::Initialize()
if (!::Renderer::Initialize())
return false;
m_bounding_box = std::make_unique<BoundingBox>();
if (!m_bounding_box->Initialize())
{
PanicAlertFmt("Failed to initialize bounding box.");
return false;
}
// Various initialization routines will have executed commands on the command buffer.
// Execute what we have done before beginning the first frame.
ExecuteCommandBuffer(true, false);
@ -132,20 +126,9 @@ void Renderer::SetPipeline(const AbstractPipeline* pipeline)
StateTracker::GetInstance()->SetPipeline(static_cast<const VKPipeline*>(pipeline));
}
u16 Renderer::BBoxReadImpl(int index)
std::unique_ptr<BoundingBox> Renderer::CreateBoundingBox() const
{
return static_cast<u16>(m_bounding_box->Get(index));
}
void Renderer::BBoxWriteImpl(int index, u16 value)
{
m_bounding_box->Set(index, value);
}
void Renderer::BBoxFlushImpl()
{
m_bounding_box->Flush();
m_bounding_box->Invalidate();
return std::make_unique<VKBoundingBox>();
}
void Renderer::ClearScreen(const MathUtil::Rectangle<int>& rc, bool color_enable, bool alpha_enable,

View File

@ -12,11 +12,11 @@
#include "VideoBackends/Vulkan/Constants.h"
#include "VideoCommon/RenderBase.h"
class BoundingBox;
struct XFBSourceBase;
namespace Vulkan
{
class BoundingBox;
class SwapChain;
class StagingTexture2D;
class VKFramebuffer;
@ -55,10 +55,6 @@ public:
size_t cache_data_length = 0) override;
SwapChain* GetSwapChain() const { return m_swap_chain.get(); }
BoundingBox* GetBoundingBox() const { return m_bounding_box.get(); }
u16 BBoxReadImpl(int index) override;
void BBoxWriteImpl(int index, u16 value) override;
void BBoxFlushImpl() override;
void Flush() override;
void WaitForGPUIdle() override;
@ -92,6 +88,9 @@ public:
// next render. Use when you want to kick the current buffer to make room for new data.
void ExecuteCommandBuffer(bool execute_off_thread, bool wait_for_completion = false);
protected:
std::unique_ptr<BoundingBox> CreateBoundingBox() const override;
private:
void CheckForSurfaceChange();
void CheckForSurfaceResize();
@ -102,7 +101,6 @@ private:
void BindFramebuffer(VKFramebuffer* fb);
std::unique_ptr<SwapChain> m_swap_chain;
std::unique_ptr<BoundingBox> m_bounding_box;
// Keep a copy of sampler states to avoid cache lookups every draw
std::array<SamplerState, NUM_PIXEL_SHADER_SAMPLERS> m_sampler_states = {};