diff --git a/src/video_core/renderer_vulkan/vk_rasterizer_cache.cpp b/src/video_core/renderer_vulkan/vk_rasterizer_cache.cpp index ff4bdf2c2..8ef4b8ae2 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer_cache.cpp @@ -205,7 +205,7 @@ VKTexture RasterizerCacheVulkan::AllocateSurfaceTexture(vk::Format format, u32 w return texture; } -/*static bool FillSurface(const Surface& surface, const u8* fill_data, +static bool FillSurface(const Surface& surface, const u8* fill_data, const Common::Rectangle& fill_rect) { OpenGLState prev_state = OpenGLState::GetCurState(); SCOPE_EXIT({ prev_state.Apply(); }); @@ -223,16 +223,11 @@ VKTexture RasterizerCacheVulkan::AllocateSurfaceTexture(vk::Format format, u32 w surface->InvalidateAllWatcher(); if (surface->type == SurfaceType::Color || surface->type == SurfaceType::Texture) { - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - surface->texture.handle, 0); - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, - 0); - Pica::Texture::TextureInfo tex_info{}; tex_info.format = static_cast(surface->pixel_format); Common::Vec4 color = Pica::Texture::LookupTexture(fill_data, 0, 0, tex_info); - std::array color_values = {color.x / 255.f, color.y / 255.f, color.z / 255.f, + std::array color_values = {color.x / 255.f, color.y / 255.f, color.z / 255.f, color.w / 255.f}; state.color_mask.red_enabled = GL_TRUE; @@ -278,7 +273,7 @@ VKTexture RasterizerCacheVulkan::AllocateSurfaceTexture(vk::Format format, u32 w glClearBufferfi(GL_DEPTH_STENCIL, 0, value_float, value_int); } return true; -}*/ +} CachedSurface::~CachedSurface() { if (texture.IsValid()) { diff --git a/src/video_core/renderer_vulkan/vk_resource_cache.cpp b/src/video_core/renderer_vulkan/vk_resource_cache.cpp index 15f2fa842..c5cea0de2 100644 --- a/src/video_core/renderer_vulkan/vk_resource_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_resource_cache.cpp @@ -10,15 +10,13 @@ namespace Vulkan { -VKResourceCache::~VKResourceCache() -{ +VKResourceCache::~VKResourceCache() { for (int i = 0; i < DESCRIPTOR_SET_LAYOUT_COUNT; i++) { g_vk_instace->GetDevice().destroyDescriptorSetLayout(descriptor_layouts[i]); } } -bool VKResourceCache::Initialize() -{ +bool VKResourceCache::Initialize() { // Define the descriptor sets we will be using std::array ubo_set = {{ { 0, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eVertex | @@ -54,58 +52,22 @@ bool VKResourceCache::Initialize() vk::PipelineLayoutCreateInfo layout_info({}, descriptor_layouts); pipeline_layout = g_vk_instace->GetDevice().createPipelineLayoutUnique(layout_info); - // Create global texture staging buffer - texture_upload_buffer.Create(MAX_TEXTURE_UPLOAD_BUFFER_SIZE, - vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent, - vk::BufferUsageFlagBits::eTransferSrc); - return true; } -vk::Sampler VKResourceCache::GetSampler(const SamplerInfo& info) -{ - auto iter = sampler_cache.find(info); - if (iter != sampler_cache.end()) { - return iter->second; - } - - // Create texture sampler - auto properties = g_vk_instace->GetPhysicalDevice().getProperties(); - auto features = g_vk_instace->GetPhysicalDevice().getFeatures(); - vk::SamplerCreateInfo sampler_info - ( - {}, - info.mag_filter, - info.min_filter, - info.mipmap_mode, - info.wrapping[0], info.wrapping[1], info.wrapping[2], - {}, - features.samplerAnisotropy, - properties.limits.maxSamplerAnisotropy, - false, - vk::CompareOp::eAlways, - {}, - {}, - vk::BorderColor::eFloatTransparentBlack, - false - ); - - auto sampler = g_vk_instace->GetDevice().createSamplerUnique(sampler_info); - vk::Sampler handle = sampler.get(); - - // Store it even if it failed - sampler_cache.emplace(info, std::move(sampler)); - return handle; -} - vk::RenderPass VKResourceCache::GetRenderPass(vk::Format color_format, vk::Format depth_format, - u32 multisamples, vk::AttachmentLoadOp load_op) -{ + vk::SampleCountFlagBits multisamples, + vk::AttachmentLoadOp load_op) { // Search the cache if we can reuse an already created renderpass - auto key = std::tie(color_format, depth_format, multisamples, load_op); - auto it = render_pass_cache.find(key); - if (it != render_pass_cache.end()) { - return it->second; + RenderPassCacheKey key = { + .color = color_format, + .depth = depth_format, + .samples = multisamples + }; + + auto it = renderpass_cache.find(key); + if (it != renderpass_cache.end()) { + return it->second.get(); } // Otherwise create a new one with the parameters provided @@ -120,7 +82,7 @@ vk::RenderPass VKResourceCache::GetRenderPass(vk::Format color_format, vk::Forma { {}, color_format, - static_cast(multisamples), + multisamples, load_op, vk::AttachmentStoreOp::eStore, vk::AttachmentLoadOp::eDontCare, @@ -157,7 +119,7 @@ vk::RenderPass VKResourceCache::GetRenderPass(vk::Format color_format, vk::Forma auto renderpass = g_vk_instace->GetDevice().createRenderPassUnique(renderpass_info); vk::RenderPass handle = renderpass.get(); - render_pass_cache.emplace(key, std::move(renderpass)); + renderpass_cache.emplace(key, std::move(renderpass)); return handle; } } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_resource_cache.h b/src/video_core/renderer_vulkan/vk_resource_cache.h index d2c65dc0b..330d731ec 100644 --- a/src/video_core/renderer_vulkan/vk_resource_cache.h +++ b/src/video_core/renderer_vulkan/vk_resource_cache.h @@ -15,13 +15,16 @@ namespace Vulkan { -using RenderPassCacheKey = std::tuple; +struct RenderPassCacheKey { + vk::Format color, depth; + vk::SampleCountFlagBits samples; +}; -constexpr u32 MAX_TEXTURE_UPLOAD_BUFFER_SIZE = 32 * 1024 * 1024; constexpr u32 DESCRIPTOR_SET_LAYOUT_COUNT = 3; -class VKResourceCache -{ +/// Wrapper class that manages resource caching and storage. +/// It stores pipelines and renderpasses +class VKResourceCache { public: VKResourceCache() = default; ~VKResourceCache(); @@ -31,9 +34,12 @@ public: void Shutdown(); // Public interface. - vk::Sampler GetSampler(const SamplerInfo& info); - vk::RenderPass GetRenderPass(vk::Format color_format, vk::Format depth_format, u32 multisamples, vk::AttachmentLoadOp load_op); vk::PipelineCache GetPipelineCache() const { return pipeline_cache.get(); } + vk::RenderPass GetRenderPass(vk::Format color_format, vk::Format depth_format, + vk::SampleCountFlagBits multisamples, + vk::AttachmentLoadOp load_op); + + auto& GetDescriptorLayouts() const { return descriptor_layouts; } private: // Descriptor sets @@ -41,8 +47,7 @@ private: vk::UniquePipelineLayout pipeline_layout; // Render pass cache - std::unordered_map render_pass_cache; - std::unordered_map sampler_cache; + std::unordered_map renderpass_cache; vk::UniquePipelineCache pipeline_cache; std::string pipeline_cache_filename; diff --git a/src/video_core/renderer_vulkan/vk_state.cpp b/src/video_core/renderer_vulkan/vk_state.cpp index 2f8427c3c..8a0cde6c0 100644 --- a/src/video_core/renderer_vulkan/vk_state.cpp +++ b/src/video_core/renderer_vulkan/vk_state.cpp @@ -3,6 +3,8 @@ // Refer to the license.txt file included. #include "video_core/renderer_vulkan/vk_state.h" +#include "video_core/renderer_vulkan/vk_task_scheduler.h" +#include "video_core/renderer_vulkan/vk_resource_cache.h" namespace Vulkan { @@ -16,6 +18,11 @@ DirtyState operator |=(DirtyState lhs, DirtyState rhs) { ); } +bool operator &(DirtyState lhs, DirtyState rhs) { + return static_cast(lhs) & + static_cast(rhs); +} + void VulkanState::Create() { // Create a dummy texture which can be used in place of a real binding. VKTexture::Info info = { @@ -27,7 +34,24 @@ void VulkanState::Create() { }; dummy_texture.Create(info); - //dummy_texture.TransitionLayout(vk::ImageLayout::eShaderReadOnlyOptimal); + dummy_texture.TransitionLayout(vk::ImageLayout::eShaderReadOnlyOptimal, g_vk_task_scheduler->GetCommandBuffer()); + + // Create descriptor pool + // TODO: Choose sizes more wisely + const std::array pool_sizes{{ + { vk::DescriptorType::eUniformBuffer, 32 }, + { vk::DescriptorType::eCombinedImageSampler, 32 }, + { vk::DescriptorType::eStorageTexelBuffer, 32 }, + }}; + + auto& device = g_vk_instace->GetDevice(); + vk::DescriptorPoolCreateInfo pool_create_info({}, 1024, pool_sizes); + desc_pool = device.createDescriptorPoolUnique(pool_create_info); + + // Create descriptor sets + auto& layouts = g_vk_res_cache->GetDescriptorLayouts(); + vk::DescriptorSetAllocateInfo alloc_info(desc_pool.get(), layouts); + descriptor_sets = device.allocateDescriptorSetsUnique(alloc_info); dirty_flags |= DirtyState::All; } @@ -38,31 +62,19 @@ void VulkanState::SetVertexBuffer(VKBuffer* buffer, vk::DeviceSize offset) { } vertex_buffer = buffer; - vertex_buffer_offset = offset; + vertex_offset = offset; dirty_flags |= DirtyState::VertexBuffer; } -void VulkanState::SetFramebuffer(VKFramebuffer* buffer) { - // Should not be changed within a render pass. - //ASSERT(!InRenderPass()); - //framebuffer = buffer; -} - -void VulkanState::SetPipeline(const VKPipeline* new_pipeline) { - if (new_pipeline == pipeline) - return; - - pipeline = new_pipeline; - dirty_flags |= DirtyState::Pipeline; -} - void VulkanState::SetUniformBuffer(UniformID id, VKBuffer* buffer, u32 offset, u32 size) { - auto& binding = bindings.ubo[static_cast(id)]; + u32 index = static_cast(id); + auto& binding = bindings.ubo[index]; if (binding.buffer != buffer->GetBuffer() || binding.range != size) { binding.buffer = buffer->GetBuffer(); binding.range = size; dirty_flags |= DirtyState::Uniform; + bindings.ubo_update[index] = true; } } @@ -75,6 +87,7 @@ void VulkanState::SetTexture(TextureID id, VKTexture* texture) { bindings.texture[index].imageView = texture->GetView(); bindings.texture[index].imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal; dirty_flags |= DirtyState::Texture; + bindings.texture_update[index] = true; } void VulkanState::SetTexelBuffer(TexelBufferID id, VKBuffer* buffer) { @@ -85,10 +98,7 @@ void VulkanState::SetTexelBuffer(TexelBufferID id, VKBuffer* buffer) { bindings.lut[index].buffer = buffer->GetBuffer(); dirty_flags |= DirtyState::TexelBuffer; -} - -void VulkanState::SetImageTexture(VKTexture* image) { - // TODO + bindings.lut_update[index] = true; } void VulkanState::UnbindTexture(VKTexture* image) { @@ -102,468 +112,140 @@ void VulkanState::UnbindTexture(VKTexture* image) { } } -void VulkanState::BeginRenderPass() -{ - if (InRenderPass()) - return; - - m_current_render_pass = m_framebuffer->GetLoadRenderPass(); - m_framebuffer_render_area = m_framebuffer->GetRect(); - - VkRenderPassBeginInfo begin_info = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, - nullptr, - m_current_render_pass, - m_framebuffer->GetFB(), - m_framebuffer_render_area, - 0, - nullptr}; - - vkCmdBeginRenderPass(g_command_buffer_mgr->GetCurrentCommandBuffer(), &begin_info, - VK_SUBPASS_CONTENTS_INLINE); +void VulkanState::SetAttachments(VKTexture* color, VKTexture* depth_stencil) { + color_attachment = color; + depth_attachment = depth_stencil; } -void StateTracker::BeginDiscardRenderPass() -{ - if (InRenderPass()) - return; - - m_current_render_pass = m_framebuffer->GetDiscardRenderPass(); - m_framebuffer_render_area = m_framebuffer->GetRect(); - - VkRenderPassBeginInfo begin_info = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, - nullptr, - m_current_render_pass, - m_framebuffer->GetFB(), - m_framebuffer_render_area, - 0, - nullptr}; - - vkCmdBeginRenderPass(g_command_buffer_mgr->GetCurrentCommandBuffer(), &begin_info, - VK_SUBPASS_CONTENTS_INLINE); +void VulkanState::SetRenderArea(vk::Rect2D new_render_area) { + render_area = new_render_area; } -void StateTracker::EndRenderPass() -{ - if (!InRenderPass()) - return; - - vkCmdEndRenderPass(g_command_buffer_mgr->GetCurrentCommandBuffer()); - m_current_render_pass = VK_NULL_HANDLE; -} - -void StateTracker::BeginClearRenderPass(const VkRect2D& area, const VkClearValue* clear_values, - u32 num_clear_values) -{ - ASSERT(!InRenderPass()); - - m_current_render_pass = m_framebuffer->GetClearRenderPass(); - m_framebuffer_render_area = area; - - VkRenderPassBeginInfo begin_info = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, - nullptr, - m_current_render_pass, - m_framebuffer->GetFB(), - m_framebuffer_render_area, - num_clear_values, - clear_values}; - - vkCmdBeginRenderPass(g_command_buffer_mgr->GetCurrentCommandBuffer(), &begin_info, - VK_SUBPASS_CONTENTS_INLINE); -} - -void StateTracker::SetViewport(const VkViewport& viewport) -{ - if (memcmp(&m_viewport, &viewport, sizeof(viewport)) == 0) - return; - - m_viewport = viewport; - m_dirty_flags |= DIRTY_FLAG_VIEWPORT; -} - -void StateTracker::SetScissor(const VkRect2D& scissor) -{ - if (memcmp(&m_scissor, &scissor, sizeof(scissor)) == 0) - return; - - m_scissor = scissor; - m_dirty_flags |= DIRTY_FLAG_SCISSOR; -} - -bool StateTracker::Bind() -{ - // Must have a pipeline. - if (!m_pipeline) - return false; - - // Check the render area if we were in a clear pass. - if (m_current_render_pass == m_framebuffer->GetClearRenderPass() && !IsViewportWithinRenderArea()) - EndRenderPass(); - - // Get a new descriptor set if any parts have changed - if (!UpdateDescriptorSet()) - { - // We can fail to allocate descriptors if we exhaust the pool for this command buffer. - WARN_LOG_FMT(VIDEO, "Failed to get a descriptor set, executing buffer"); - Renderer::GetInstance()->ExecuteCommandBuffer(false, false); - if (!UpdateDescriptorSet()) - { - // Something strange going on. - ERROR_LOG_FMT(VIDEO, "Failed to get descriptor set, skipping draw"); - return false; - } - } - - // Start render pass if not already started - if (!InRenderPass()) - BeginRenderPass(); - - // Re-bind parts of the pipeline - const VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer(); - if (m_dirty_flags & DIRTY_FLAG_VERTEX_BUFFER) - vkCmdBindVertexBuffers(command_buffer, 0, 1, &m_vertex_buffer, &m_vertex_buffer_offset); - - if (m_dirty_flags & DIRTY_FLAG_INDEX_BUFFER) - vkCmdBindIndexBuffer(command_buffer, m_index_buffer, m_index_buffer_offset, m_index_type); - - if (m_dirty_flags & DIRTY_FLAG_PIPELINE) - vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline->GetVkPipeline()); - - if (m_dirty_flags & DIRTY_FLAG_VIEWPORT) - vkCmdSetViewport(command_buffer, 0, 1, &m_viewport); - - if (m_dirty_flags & DIRTY_FLAG_SCISSOR) - vkCmdSetScissor(command_buffer, 0, 1, &m_scissor); - - m_dirty_flags &= ~(DIRTY_FLAG_VERTEX_BUFFER | DIRTY_FLAG_INDEX_BUFFER | DIRTY_FLAG_PIPELINE | - DIRTY_FLAG_VIEWPORT | DIRTY_FLAG_SCISSOR); - return true; -} - -bool StateTracker::BindCompute() -{ - if (!m_compute_shader) - return false; - - // Can't kick compute in a render pass. - if (InRenderPass()) - EndRenderPass(); - - const VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer(); - if (m_dirty_flags & DIRTY_FLAG_COMPUTE_SHADER) - { - vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, - m_compute_shader->GetComputePipeline()); - } - - if (!UpdateComputeDescriptorSet()) - { - WARN_LOG_FMT(VIDEO, "Failed to get a compute descriptor set, executing buffer"); - Renderer::GetInstance()->ExecuteCommandBuffer(false, false); - if (!UpdateComputeDescriptorSet()) - { - // Something strange going on. - ERROR_LOG_FMT(VIDEO, "Failed to get descriptor set, skipping dispatch"); - return false; - } - } - - m_dirty_flags &= ~DIRTY_FLAG_COMPUTE_SHADER; - return true; -} - -bool StateTracker::IsWithinRenderArea(s32 x, s32 y, u32 width, u32 height) const -{ - // Check that the viewport does not lie outside the render area. - // If it does, we need to switch to a normal load/store render pass. - s32 left = m_framebuffer_render_area.offset.x; - s32 top = m_framebuffer_render_area.offset.y; - s32 right = left + static_cast(m_framebuffer_render_area.extent.width); - s32 bottom = top + static_cast(m_framebuffer_render_area.extent.height); - s32 test_left = x; - s32 test_top = y; - s32 test_right = test_left + static_cast(width); - s32 test_bottom = test_top + static_cast(height); - return test_left >= left && test_right <= right && test_top >= top && test_bottom <= bottom; -} - -bool StateTracker::IsViewportWithinRenderArea() const -{ - return IsWithinRenderArea(static_cast(m_viewport.x), static_cast(m_viewport.y), - static_cast(m_viewport.width), - static_cast(m_viewport.height)); -} - -void StateTracker::EndClearRenderPass() -{ - if (m_current_render_pass != m_framebuffer->GetClearRenderPass()) - return; - - // End clear render pass. Bind() will call BeginRenderPass() which - // will switch to the load/store render pass. - EndRenderPass(); -} - -bool StateTracker::UpdateDescriptorSet() -{ - if (m_pipeline->GetUsage() == AbstractPipelineUsage::GX) - return UpdateGXDescriptorSet(); - else - return UpdateUtilityDescriptorSet(); -} - -bool StateTracker::UpdateGXDescriptorSet() -{ - const size_t MAX_DESCRIPTOR_WRITES = NUM_UBO_DESCRIPTOR_SET_BINDINGS + // UBO - 1 + // Samplers - 1; // SSBO - std::array writes; - u32 num_writes = 0; - - if (m_dirty_flags & DIRTY_FLAG_GX_UBOS || m_gx_descriptor_sets[0] == VK_NULL_HANDLE) - { - m_gx_descriptor_sets[0] = g_command_buffer_mgr->AllocateDescriptorSet( - g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_STANDARD_UNIFORM_BUFFERS)); - if (m_gx_descriptor_sets[0] == VK_NULL_HANDLE) - return false; - - for (size_t i = 0; i < NUM_UBO_DESCRIPTOR_SET_BINDINGS; i++) - { - if (i == UBO_DESCRIPTOR_SET_BINDING_GS && - !g_ActiveConfig.backend_info.bSupportsGeometryShaders) - { - continue; - } - - writes[num_writes++] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - nullptr, - m_gx_descriptor_sets[0], - static_cast(i), - 0, - 1, - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, - nullptr, - &m_bindings.gx_ubo_bindings[i], - nullptr}; +void VulkanState::BeginRendering() { + if (rendering) { + return; } - m_dirty_flags = (m_dirty_flags & ~DIRTY_FLAG_GX_UBOS) | DIRTY_FLAG_DESCRIPTOR_SETS; - } + // Make sure attachments are in optimal layout + auto command_buffer = g_vk_task_scheduler->GetCommandBuffer(); + if (color_attachment->GetLayout() != vk::ImageLayout::eColorAttachmentOptimal) { + color_attachment->TransitionLayout(vk::ImageLayout::eColorAttachmentOptimal, command_buffer); + } - if (m_dirty_flags & DIRTY_FLAG_GX_SAMPLERS || m_gx_descriptor_sets[1] == VK_NULL_HANDLE) - { - m_gx_descriptor_sets[1] = g_command_buffer_mgr->AllocateDescriptorSet( - g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_STANDARD_SAMPLERS)); - if (m_gx_descriptor_sets[1] == VK_NULL_HANDLE) - return false; + if (depth_attachment->GetLayout() != vk::ImageLayout::eDepthStencilAttachmentOptimal) { + depth_attachment->TransitionLayout(vk::ImageLayout::eDepthStencilAttachmentOptimal, command_buffer); + } - writes[num_writes++] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - nullptr, - m_gx_descriptor_sets[1], - 0, - 0, - static_cast(NUM_PIXEL_SHADER_SAMPLERS), - VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - m_bindings.samplers.data(), - nullptr, - nullptr}; - m_dirty_flags = (m_dirty_flags & ~DIRTY_FLAG_GX_SAMPLERS) | DIRTY_FLAG_DESCRIPTOR_SETS; - } + // Begin rendering + vk::RenderingAttachmentInfoKHR color_info(color_attachment->GetView(), color_attachment->GetLayout()); + vk::RenderingAttachmentInfoKHR depth_stencil_info(depth_attachment->GetView(), depth_attachment->GetLayout()); - if (g_ActiveConfig.backend_info.bSupportsBBox && - (m_dirty_flags & DIRTY_FLAG_GX_SSBO || m_gx_descriptor_sets[2] == VK_NULL_HANDLE)) - { - m_gx_descriptor_sets[2] = - g_command_buffer_mgr->AllocateDescriptorSet(g_object_cache->GetDescriptorSetLayout( - DESCRIPTOR_SET_LAYOUT_STANDARD_SHADER_STORAGE_BUFFERS)); - if (m_gx_descriptor_sets[2] == VK_NULL_HANDLE) - return false; + vk::RenderingInfo render_info + ( + {}, render_area, 1, {}, + color_info, + &depth_stencil_info, + &depth_stencil_info + ); - writes[num_writes++] = { - VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, m_gx_descriptor_sets[2], 0, 0, 1, - VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &m_bindings.ssbo, nullptr}; - m_dirty_flags = (m_dirty_flags & ~DIRTY_FLAG_GX_SSBO) | DIRTY_FLAG_DESCRIPTOR_SETS; - } - - if (num_writes > 0) - vkUpdateDescriptorSets(g_vulkan_context->GetDevice(), num_writes, writes.data(), 0, nullptr); - - if (m_dirty_flags & DIRTY_FLAG_DESCRIPTOR_SETS) - { - vkCmdBindDescriptorSets(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline->GetVkPipelineLayout(), 0, - g_ActiveConfig.backend_info.bSupportsBBox ? - NUM_GX_DESCRIPTOR_SETS : - (NUM_GX_DESCRIPTOR_SETS - 1), - m_gx_descriptor_sets.data(), - g_ActiveConfig.backend_info.bSupportsGeometryShaders ? - NUM_UBO_DESCRIPTOR_SET_BINDINGS : - (NUM_UBO_DESCRIPTOR_SET_BINDINGS - 1), - m_bindings.gx_ubo_offsets.data()); - m_dirty_flags &= ~(DIRTY_FLAG_DESCRIPTOR_SETS | DIRTY_FLAG_GX_UBO_OFFSETS); - } - else if (m_dirty_flags & DIRTY_FLAG_GX_UBO_OFFSETS) - { - vkCmdBindDescriptorSets(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline->GetVkPipelineLayout(), 0, - 1, m_gx_descriptor_sets.data(), - g_ActiveConfig.backend_info.bSupportsGeometryShaders ? - NUM_UBO_DESCRIPTOR_SET_BINDINGS : - (NUM_UBO_DESCRIPTOR_SET_BINDINGS - 1), - m_bindings.gx_ubo_offsets.data()); - m_dirty_flags &= ~DIRTY_FLAG_GX_UBO_OFFSETS; - } - - return true; + command_buffer.beginRendering(render_info); + rendering = true; } -bool StateTracker::UpdateUtilityDescriptorSet() -{ - // Max number of updates - UBO, Samplers, TexelBuffer - std::array dswrites; - u32 writes = 0; +void VulkanState::EndRendering() { + if (!rendering) { + return; + } - // Allocate descriptor sets. - if (m_dirty_flags & DIRTY_FLAG_UTILITY_UBO || m_utility_descriptor_sets[0] == VK_NULL_HANDLE) - { - m_utility_descriptor_sets[0] = g_command_buffer_mgr->AllocateDescriptorSet( - g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_UTILITY_UNIFORM_BUFFER)); - if (!m_utility_descriptor_sets[0]) - return false; - - dswrites[writes++] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - nullptr, - m_utility_descriptor_sets[0], - 0, - 0, - 1, - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, - nullptr, - &m_bindings.utility_ubo_binding, - nullptr}; - - m_dirty_flags = (m_dirty_flags & ~DIRTY_FLAG_UTILITY_UBO) | DIRTY_FLAG_DESCRIPTOR_SETS; - } - - if (m_dirty_flags & DIRTY_FLAG_UTILITY_BINDINGS || m_utility_descriptor_sets[1] == VK_NULL_HANDLE) - { - m_utility_descriptor_sets[1] = g_command_buffer_mgr->AllocateDescriptorSet( - g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_UTILITY_SAMPLERS)); - if (!m_utility_descriptor_sets[1]) - return false; - - dswrites[writes++] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - nullptr, - m_utility_descriptor_sets[1], - 0, - 0, - NUM_PIXEL_SHADER_SAMPLERS, - VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - m_bindings.samplers.data(), - nullptr, - nullptr}; - dswrites[writes++] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - nullptr, - m_utility_descriptor_sets[1], - 8, - 0, - 1, - VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, - nullptr, - nullptr, - m_bindings.texel_buffers.data()}; - - m_dirty_flags = (m_dirty_flags & ~DIRTY_FLAG_UTILITY_BINDINGS) | DIRTY_FLAG_DESCRIPTOR_SETS; - } - - if (writes > 0) - vkUpdateDescriptorSets(g_vulkan_context->GetDevice(), writes, dswrites.data(), 0, nullptr); - - if (m_dirty_flags & DIRTY_FLAG_DESCRIPTOR_SETS) - { - vkCmdBindDescriptorSets(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline->GetVkPipelineLayout(), 0, - NUM_UTILITY_DESCRIPTOR_SETS, m_utility_descriptor_sets.data(), 1, - &m_bindings.utility_ubo_offset); - m_dirty_flags &= ~(DIRTY_FLAG_DESCRIPTOR_SETS | DIRTY_FLAG_UTILITY_UBO_OFFSET); - } - else if (m_dirty_flags & DIRTY_FLAG_UTILITY_UBO_OFFSET) - { - vkCmdBindDescriptorSets(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline->GetVkPipelineLayout(), 0, - 1, m_utility_descriptor_sets.data(), 1, &m_bindings.utility_ubo_offset); - m_dirty_flags &= ~(DIRTY_FLAG_DESCRIPTOR_SETS | DIRTY_FLAG_UTILITY_UBO_OFFSET); - } - - return true; + auto command_buffer = g_vk_task_scheduler->GetCommandBuffer(); + command_buffer.endRendering(); + rendering = false; } -bool StateTracker::UpdateComputeDescriptorSet() -{ - // Max number of updates - UBO, Samplers, TexelBuffer, Image - std::array dswrites; +void VulkanState::SetViewport(vk::Viewport new_viewport) { + if (new_viewport == viewport) { + return; + } - // Allocate descriptor sets. - if (m_dirty_flags & DIRTY_FLAG_COMPUTE_BINDINGS) - { - m_compute_descriptor_set = g_command_buffer_mgr->AllocateDescriptorSet( - g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_COMPUTE)); - dswrites[0] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - nullptr, - m_compute_descriptor_set, - 0, - 0, - 1, - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, - nullptr, - &m_bindings.utility_ubo_binding, - nullptr}; - dswrites[1] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - nullptr, - m_compute_descriptor_set, - 1, - 0, - NUM_COMPUTE_SHADER_SAMPLERS, - VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - m_bindings.samplers.data(), - nullptr, - nullptr}; - dswrites[2] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - nullptr, - m_compute_descriptor_set, - 3, - 0, - NUM_COMPUTE_TEXEL_BUFFERS, - VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, - nullptr, - nullptr, - m_bindings.texel_buffers.data()}; - dswrites[3] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - nullptr, - m_compute_descriptor_set, - 5, - 0, - 1, - VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, - &m_bindings.image_texture, - nullptr, - nullptr}; + viewport = new_viewport; + dirty_flags |= DirtyState::Viewport; +} - vkUpdateDescriptorSets(g_vulkan_context->GetDevice(), static_cast(dswrites.size()), - dswrites.data(), 0, nullptr); - m_dirty_flags = - (m_dirty_flags & ~DIRTY_FLAG_COMPUTE_BINDINGS) | DIRTY_FLAG_COMPUTE_DESCRIPTOR_SET; - } +void VulkanState::SetScissor(vk::Rect2D new_scissor) { + if (new_scissor == scissor) { + return; + } - if (m_dirty_flags & DIRTY_FLAG_COMPUTE_DESCRIPTOR_SET) - { - vkCmdBindDescriptorSets(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_PIPELINE_BIND_POINT_COMPUTE, - g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_COMPUTE), 0, 1, - &m_compute_descriptor_set, 1, &m_bindings.utility_ubo_offset); - m_dirty_flags &= ~DIRTY_FLAG_COMPUTE_DESCRIPTOR_SET; - } + scissor = new_scissor; + dirty_flags |= DirtyState::Scissor; +} - return true; +void VulkanState::Apply() { + // Update resources in descriptor sets if changed + UpdateDescriptorSet(); + + // Start rendering if not already started + BeginRendering(); + + // Re-apply dynamic parts of the pipeline + auto command_buffer = g_vk_task_scheduler->GetCommandBuffer(); + if (dirty_flags & DirtyState::VertexBuffer) { + command_buffer.bindVertexBuffers(0, vertex_buffer->GetBuffer(), vertex_offset); + } + + if (dirty_flags & DirtyState::IndexBuffer) { + command_buffer.bindIndexBuffer(index_buffer->GetBuffer(), index_offset, vk::IndexType::eUint16); + } + + if (dirty_flags & DirtyState::Viewport) { + command_buffer.setViewport(0, viewport); + } + + if (dirty_flags & DirtyState::Scissor) { + command_buffer.setScissor(0, scissor); + } + + dirty_flags = DirtyState::None; +} + +void VulkanState::UpdateDescriptorSet() { + std::vector writes; + auto& device = g_vk_instace->GetDevice(); + + // Check if any resource has been updated + if (dirty_flags & DirtyState::Uniform) { + for (int i = 0; i < 2; i++) { + if (bindings.ubo_update[i]) { + writes.emplace_back(descriptor_sets[i].get(), i, 0, 1, vk::DescriptorType::eUniformBuffer, + nullptr, &bindings.ubo[i]); + bindings.ubo_update[i] = false; + } + } + } + + if (dirty_flags & DirtyState::Texture) { + for (int i = 0; i < 4; i++) { + if (bindings.texture_update[i]) { + writes.emplace_back(descriptor_sets[i].get(), i, 0, 1, vk::DescriptorType::eCombinedImageSampler, + nullptr, &bindings.texture[i]); + bindings.texture_update[i] = false; + } + } + } + + if (dirty_flags & DirtyState::TexelBuffer) { + for (int i = 0; i < 3; i++) { + if (bindings.lut_update[i]) { + writes.emplace_back(descriptor_sets[i].get(), i, 0, 1, vk::DescriptorType::eStorageTexelBuffer, + nullptr, &bindings.lut[i]); + bindings.lut_update[i] = false; + } + } + } + + if (!writes.empty()) { + device.updateDescriptorSets(writes, {}); + } } } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_state.h b/src/video_core/renderer_vulkan/vk_state.h index af9ec53b7..72dd23454 100644 --- a/src/video_core/renderer_vulkan/vk_state.h +++ b/src/video_core/renderer_vulkan/vk_state.h @@ -6,12 +6,11 @@ #include #include "video_core/renderer_vulkan/vk_texture.h" -#include "video_core/renderer_vulkan/vk_pipeline.h" namespace Vulkan { enum class DirtyState { - All, + None, Framebuffer, Pipeline, Texture, @@ -25,7 +24,9 @@ enum class DirtyState { Scissor, CullMode, VertexBuffer, - Uniform + IndexBuffer, + Uniform, + All }; enum class UniformID { @@ -57,29 +58,33 @@ public: /// Configure drawing state void SetVertexBuffer(VKBuffer* buffer, vk::DeviceSize offset); - void SetFramebuffer(VKFramebuffer* framebuffer); - void SetPipeline(const VKPipeline* pipeline); + void SetViewport(vk::Viewport viewport); + void SetScissor(vk::Rect2D scissor); + + /// Rendering + void SetAttachments(VKTexture* color, VKTexture* depth_stencil); + void SetRenderArea(vk::Rect2D render_area); + void BeginRendering(); + void EndRendering(); /// Configure shader resources void SetUniformBuffer(UniformID id, VKBuffer* buffer, u32 offset, u32 size); void SetTexture(TextureID id, VKTexture* texture); void SetTexelBuffer(TexelBufferID id, VKBuffer* buffer); - void SetImageTexture(VKTexture* image); void UnbindTexture(VKTexture* image); /// Apply all dirty state to the current Vulkan command buffer + void UpdateDescriptorSet(); void Apply(); private: // Stage which should be applied DirtyState dirty_flags; + bool rendering = false; // Input assembly - VKBuffer* vertex_buffer = nullptr; - vk::DeviceSize vertex_buffer_offset = 0; - - // Pipeline state - const VKPipeline* pipeline = nullptr; + VKBuffer* vertex_buffer = nullptr, * index_buffer = nullptr; + vk::DeviceSize vertex_offset = 0, index_offset = 0; // Shader bindings. These describe which resources // we have bound to the pipeline and at which @@ -89,22 +94,24 @@ private: struct { std::array ubo; + std::array ubo_update; std::array texture; + std::array texture_update; std::array lut; + std::array lut_update; } bindings = {}; - - std::array descriptor_sets = {}; + std::vector descriptor_sets = {}; + vk::UniqueDescriptorPool desc_pool; // Rasterization - vk::Viewport viewport = {0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f}; + vk::Viewport viewport = { 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f }; vk::CullModeFlags cull_mode = vk::CullModeFlagBits::eNone; - vk::Rect2D scissor = {{0, 0}, {1, 1}}; + vk::Rect2D scissor = { {0, 0}, {1, 1} }; VKTexture dummy_texture; - // Framebuffer - VKFramebuffer* framebuffer = nullptr; - vk::RenderPass current_render_pass = VK_NULL_HANDLE; - vk::Rect2D framebuffer_render_area = {}; + // Render attachments + VKTexture* color_attachment = nullptr, * depth_attachment = nullptr; + vk::Rect2D render_area = {}; vk::ColorComponentFlags color_mask; // Depth diff --git a/src/video_core/renderer_vulkan/vk_texture.cpp b/src/video_core/renderer_vulkan/vk_texture.cpp index 2dbfcec44..e98a6d9e5 100644 --- a/src/video_core/renderer_vulkan/vk_texture.cpp +++ b/src/video_core/renderer_vulkan/vk_texture.cpp @@ -51,11 +51,8 @@ void VKTexture::Create(const Info& info, bool make_staging) { LOG_CRITICAL(Render_Vulkan, "Unknown texture format {}", texture_info.format); } - // Make sure the texture size doesn't exceed the global staging buffer size - u32 image_size = texture_info.width * texture_info.height * channels; - assert(image_size <= MAX_TEXTURE_UPLOAD_BUFFER_SIZE); - // Create the texture + u32 image_size = texture_info.width * texture_info.height * channels; vk::ImageCreateFlags flags; if (info.view_type == vk::ImageViewType::eCube) { flags = vk::ImageCreateFlagBits::eCubeCompatible; @@ -103,7 +100,7 @@ void VKTexture::Adopt(vk::Image image, vk::ImageViewCreateInfo view_info) { texture_view = g_vk_instace->GetDevice().createImageView(view_info); } -void VKTexture::TransitionLayout(vk::ImageLayout new_layout, vk::CommandBuffer& command_buffer) { +void VKTexture::TransitionLayout(vk::ImageLayout new_layout, vk::CommandBuffer command_buffer) { struct LayoutInfo { vk::ImageLayout layout; vk::AccessFlags access; @@ -209,12 +206,13 @@ void VKTexture::CopyPixels(std::span new_pixels) { TransitionLayout(vk::ImageLayout::eShaderReadOnlyOptimal, command_buffer); } -void VKTexture::BlitTo(Common::Rectangle srect, VKTexture& dest, - Common::Rectangle drect, SurfaceParams::SurfaceType type, - vk::CommandBuffer& command_buffer) { +void VKTexture::BlitTo(Common::Rectangle srect, VKTexture* dest, + Common::Rectangle drect, SurfaceParams::SurfaceType type) { + auto command_buffer = g_vk_task_scheduler->GetCommandBuffer(); + // Ensure textures are of the same dimention - assert(texture_info.width == dest.texture_info.width && - texture_info.height == dest.texture_info.height); + assert(texture_info.width == dest->texture_info.width && + texture_info.height == dest->texture_info.height); vk::ImageAspectFlags image_aspect; switch (type) { @@ -242,73 +240,34 @@ void VKTexture::BlitTo(Common::Rectangle srect, VKTexture& dest, // Transition image layouts TransitionLayout(vk::ImageLayout::eTransferSrcOptimal, command_buffer); - dest.TransitionLayout(vk::ImageLayout::eTransferDstOptimal, command_buffer); + dest->TransitionLayout(vk::ImageLayout::eTransferDstOptimal, command_buffer); // Perform blit operation - command_buffer.blitImage(texture, vk::ImageLayout::eTransferSrcOptimal, dest.texture, + command_buffer.blitImage(texture, vk::ImageLayout::eTransferSrcOptimal, dest->GetHandle(), vk::ImageLayout::eTransferDstOptimal, regions, vk::Filter::eNearest); } -void VKTexture::Fill(Common::Rectangle region, glm::vec4 color) { +void VKTexture::Fill(Common::Rectangle region, vk::ImageAspectFlags aspect, + vk::ClearValue value) { + auto command_buffer = g_vk_task_scheduler->GetCommandBuffer(); + TransitionLayout(vk::ImageLayout::eTransferDstOptimal, command_buffer); -} + // End any ongoing rendering operations + g_vk_state->EndRendering(); -void VKTexture::Fill(Common::Rectangle region, glm::vec2 depth_stencil) { + // Set fill area + g_vk_state->SetAttachments(this, nullptr); -} + // Begin clear render + g_vk_state->BeginRendering(); -VKFramebuffer::~VKFramebuffer() { - auto deleter = [this]() { - if (framebuffer) { - auto& device = g_vk_instace->GetDevice(); - device.destroyFramebuffer(framebuffer); - } - }; + vk::Offset2D offset(region.left, region.bottom); + vk::Rect2D rect(offset, { region.GetWidth(), region.GetHeight() }); + vk::ClearAttachment clear_info(aspect, 0, value); + vk::ClearRect clear_rect(rect, 0, 1); + command_buffer.clearAttachments(clear_info, clear_rect); - g_vk_task_scheduler->Schedule(deleter); -} - -void VKFramebuffer::Create(const Info& info) { - // Make sure that either attachment is valid - assert(info.color || info.depth_stencil); - attachments = { info.color, info.depth_stencil }; - - auto rect = info.color ? info.color->GetRect() : info.depth_stencil->GetRect(); - auto color_format = info.color ? info.color->GetFormat() : vk::Format::eUndefined; - auto depth_format = info.depth_stencil ? info.depth_stencil->GetFormat() : vk::Format::eUndefined; - - vk::FramebufferCreateInfo framebuffer_info - ( - {}, - g_vk_res_cache->GetRenderPass(color_format, depth_format, 1, vk::AttachmentLoadOp::eLoad), - {}, - rect.extent.width, - rect.extent.height, - 1 - ); - - if (info.color && info.depth_stencil) { - std::array views = { info.color->GetView(), info.depth_stencil->GetView() }; - framebuffer_info.setAttachments(views); - } - else { - auto valid = info.color ? info.color : info.depth_stencil; - std::array view = { valid->GetView() }; - framebuffer_info.setAttachments(view); - } - - framebuffer = g_vk_instace->GetDevice().createFramebuffer(framebuffer_info); -} - -void VKFramebuffer::Prepare(vk::CommandBuffer& command_buffer) { - // Transition attachments to their optimal formats for rendering - if (attachments[Attachments::Color]) { - attachments[Attachments::Color]->TransitionLayout(vk::ImageLayout::eColorAttachmentOptimal, command_buffer); - } - - if (attachments[Attachments::DepthStencil]) { - attachments[Attachments::DepthStencil]->TransitionLayout(vk::ImageLayout::eDepthStencilAttachmentOptimal, command_buffer); - } + TransitionLayout(vk::ImageLayout::eShaderReadOnlyOptimal, command_buffer); } } diff --git a/src/video_core/renderer_vulkan/vk_texture.h b/src/video_core/renderer_vulkan/vk_texture.h index f2b0732c1..1fa1169a1 100644 --- a/src/video_core/renderer_vulkan/vk_texture.h +++ b/src/video_core/renderer_vulkan/vk_texture.h @@ -52,24 +52,24 @@ public: void CopyPixels(std::span pixels); /// Get Vulkan objects - vk::Image GetImage() const { return texture; } + vk::Image GetHandle() const { return texture; } vk::ImageView GetView() const { return texture_view; } vk::Format GetFormat() const { return texture_info.format; } vk::Rect2D GetRect() const { return vk::Rect2D({}, { texture_info.width, texture_info.height }); } + vk::ImageLayout GetLayout() const { return texture_layout; } u32 GetSamples() const { return texture_info.multisamples; } bool IsValid() { return texture; } /// Used to transition the image to an optimal layout during transfers - void TransitionLayout(vk::ImageLayout new_layout, vk::CommandBuffer& command_buffer); + void TransitionLayout(vk::ImageLayout new_layout, vk::CommandBuffer command_buffer); /// Fill the texture with the values provided - void Fill(Common::Rectangle region, glm::vec4 color); - void Fill(Common::Rectangle region, glm::vec2 depth_stencil); + void Fill(Common::Rectangle region, vk::ImageAspectFlags aspect, + vk::ClearValue value); /// Copy current texture to another with optionally performing format convesions - void BlitTo(Common::Rectangle source_rect, VKTexture& dest, - Common::Rectangle dst_rect, SurfaceParams::SurfaceType type, - vk::CommandBuffer& command_buffer); + void BlitTo(Common::Rectangle srect, VKTexture* dest, + Common::Rectangle drect, SurfaceParams::SurfaceType type); private: bool cleanup_image = true; @@ -84,34 +84,4 @@ private: VKBuffer staging; }; -enum Attachments { - Color = 0, - DepthStencil = 1 -}; - -/// Vulkan framebuffer object similar to an FBO in OpenGL -class VKFramebuffer final : public NonCopyable { -public: - struct Info { - VKTexture* color = nullptr; - VKTexture* depth_stencil = nullptr; - }; - - VKFramebuffer() = default; - ~VKFramebuffer(); - - /// Create Vulkan framebuffer object - void Create(const Info& info); - - /// Configure frambuffer for rendering - void Prepare(vk::CommandBuffer& command_buffer); - - vk::Rect2D GetRect() const { return vk::Rect2D({}, { width, height }); } - -private: - u32 width, height; - vk::Framebuffer framebuffer; - std::array attachments; -}; - } // namespace Vulkan