diff options
Diffstat (limited to 'Source/Core/VideoBackends/Vulkan/VKBoundingBox.cpp')
| -rw-r--r-- | Source/Core/VideoBackends/Vulkan/VKBoundingBox.cpp | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/Source/Core/VideoBackends/Vulkan/VKBoundingBox.cpp b/Source/Core/VideoBackends/Vulkan/VKBoundingBox.cpp new file mode 100644 index 0000000000..5412d5cb06 --- /dev/null +++ b/Source/Core/VideoBackends/Vulkan/VKBoundingBox.cpp @@ -0,0 +1,254 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include <vector> + +#include "Common/Assert.h" +#include "Common/Logging/Log.h" + +#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/VKRenderer.h" +#include "VideoBackends/Vulkan/VulkanContext.h" + +namespace Vulkan +{ +BoundingBox::BoundingBox() +{ +} + +BoundingBox::~BoundingBox() +{ + if (m_gpu_buffer != VK_NULL_HANDLE) + { + vkDestroyBuffer(g_vulkan_context->GetDevice(), m_gpu_buffer, nullptr); + vkFreeMemory(g_vulkan_context->GetDevice(), m_gpu_memory, nullptr); + } +} + +bool BoundingBox::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; + + if (!CreateReadbackBuffer()) + return false; + + // Bind bounding box to state tracker + StateTracker::GetInstance()->SetSSBO(m_gpu_buffer, 0, BUFFER_SIZE); + return true; +} + +void BoundingBox::Flush() +{ + if (m_gpu_buffer == VK_NULL_HANDLE) + return; + + // 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; + + size_t count = 0; + std::array<s32, 4> write_values; + for (; (start + count) < 4; count++) + { + if (!m_values_dirty[start + count]) + break; + + m_readback_buffer->Read((start + count) * sizeof(s32), &write_values[count], sizeof(s32), + false); + m_values_dirty[start + count] = false; + } + + // 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(); + + // 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); + + updated_buffer = true; + } + + vkCmdUpdateBuffer(g_command_buffer_mgr->GetCurrentCommandBuffer(), m_gpu_buffer, + start * sizeof(s32), count * sizeof(s32), + reinterpret_cast<const u32*>(write_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; +} + +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), ¤t_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() +{ + VkBufferUsageFlags buffer_usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | + VK_BUFFER_USAGE_TRANSFER_SRC_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT; + VkBufferCreateInfo info = { + VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType + nullptr, // const void* pNext + 0, // VkBufferCreateFlags flags + BUFFER_SIZE, // VkDeviceSize size + buffer_usage, // VkBufferUsageFlags usage + VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode + 0, // uint32_t queueFamilyIndexCount + nullptr // const uint32_t* pQueueFamilyIndices + }; + + VkBuffer buffer; + VkResult res = vkCreateBuffer(g_vulkan_context->GetDevice(), &info, nullptr, &buffer); + if (res != VK_SUCCESS) + { + LOG_VULKAN_ERROR(res, "vkCreateBuffer failed: "); + return false; + } + + VkMemoryRequirements memory_requirements; + vkGetBufferMemoryRequirements(g_vulkan_context->GetDevice(), buffer, &memory_requirements); + + uint32_t memory_type_index = g_vulkan_context + ->GetMemoryType(memory_requirements.memoryTypeBits, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, false) + .value_or(0); + VkMemoryAllocateInfo memory_allocate_info = { + VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, // VkStructureType sType + nullptr, // const void* pNext + memory_requirements.size, // VkDeviceSize allocationSize + memory_type_index // uint32_t memoryTypeIndex + }; + VkDeviceMemory memory; + res = vkAllocateMemory(g_vulkan_context->GetDevice(), &memory_allocate_info, nullptr, &memory); + if (res != VK_SUCCESS) + { + LOG_VULKAN_ERROR(res, "vkAllocateMemory failed: "); + vkDestroyBuffer(g_vulkan_context->GetDevice(), buffer, nullptr); + return false; + } + + res = vkBindBufferMemory(g_vulkan_context->GetDevice(), buffer, memory, 0); + if (res != VK_SUCCESS) + { + LOG_VULKAN_ERROR(res, "vkBindBufferMemory failed: "); + vkDestroyBuffer(g_vulkan_context->GetDevice(), buffer, nullptr); + vkFreeMemory(g_vulkan_context->GetDevice(), memory, nullptr); + return false; + } + + m_gpu_buffer = buffer; + m_gpu_memory = memory; + return true; +} + +bool BoundingBox::CreateReadbackBuffer() +{ + m_readback_buffer = StagingBuffer::Create(STAGING_BUFFER_TYPE_READBACK, BUFFER_SIZE, + VK_BUFFER_USAGE_TRANSFER_DST_BIT); + + if (!m_readback_buffer || !m_readback_buffer->Map()) + return false; + + 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, ®ion); + + // 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 |
