From b29f263e1be86fb801c1684990dda885238351f5 Mon Sep 17 00:00:00 2001 From: GPUCode Date: Wed, 25 Jan 2023 16:40:02 +0200 Subject: [PATCH] renderer_vulkan: Support additional dynamic states --- src/common/hash.h | 2 +- .../renderer_vulkan/vk_pipeline_cache.cpp | 162 ++++++++++++++---- .../renderer_vulkan/vk_pipeline_cache.h | 37 ++-- .../renderer_vulkan/vk_rasterizer.cpp | 20 +-- 4 files changed, 160 insertions(+), 61 deletions(-) diff --git a/src/common/hash.h b/src/common/hash.h index fd0128ce0..507f316e8 100644 --- a/src/common/hash.h +++ b/src/common/hash.h @@ -38,7 +38,7 @@ static inline u64 ComputeStructHash64(const T& data) noexcept { * Combines the seed parameter with the provided hash, producing a new unique hash * Implementation from: http://boost.sourceforge.net/doc/html/boost/hash_combine.html */ -inline u64 HashCombine(std::size_t& seed, const u64 hash) { +inline u64 HashCombine(std::size_t seed, const u64 hash) { return seed ^= hash + 0x9e3779b9 + (seed << 6) + (seed >> 2); } diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index a4dfe8561..c24b06baf 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #include "common/common_paths.h" #include "common/file_util.h" #include "common/logging/log.h" @@ -59,6 +60,37 @@ vk::ShaderStageFlagBits MakeShaderStage(std::size_t index) { return vk::ShaderStageFlagBits::eVertex; } +u64 PipelineInfo::Hash(const Instance& instance) const { + u64 info_hash = 0; + const auto AppendHash = [&info_hash](const auto& data) { + const u64 data_hash = Common::ComputeStructHash64(data); + info_hash = Common::HashCombine(info_hash, data_hash); + }; + + AppendHash(vertex_layout); + AppendHash(attachments); + + if (!instance.IsExtendedDynamicStateSupported()) { + AppendHash(rasterization); + AppendHash(depth_stencil); + } + if (!instance.IsExtendedDynamicState2Supported()) { + AppendHash(blending.logic_op); + } + if (!instance.IsExtendedDynamicState3LogicOpSupported() || + !instance.IsExtendedDynamicState3BlendEnableSupported()) { + AppendHash(blending.blend_enable); + } + if (!instance.IsExtendedDynamicState3BlendEqSupported()) { + AppendHash(blending.value); + } + if (!instance.IsExtendedDynamicState3ColorMaskSupported()) { + AppendHash(blending.color_write_mask); + } + + return info_hash; +} + PipelineCache::Shader::Shader(const Instance& instance) : device{instance.GetDevice()} {} PipelineCache::Shader::Shader(const Instance& instance, vk::ShaderStageFlagBits stage, @@ -80,8 +112,8 @@ PipelineCache::GraphicsPipeline::GraphicsPipeline( Common::ThreadWorker* worker_) : instance{instance_}, worker{worker_}, pipeline_layout{layout_}, pipeline_cache{pipeline_cache_}, info{info_}, stages{stages_}, - renderpass{ - renderpass_cache_.GetRenderpass(info.color_attachment, info.depth_attachment, false)} { + renderpass{renderpass_cache_.GetRenderpass(info.attachments.color_format, + info.attachments.depth_format, false)} { // Ask the driver if it can give us the pipeline quickly if (Build(true)) { @@ -170,7 +202,7 @@ bool PipelineCache::GraphicsPipeline::Build(bool fail_on_compile_required) { }; const vk::PipelineColorBlendAttachmentState colorblend_attachment = { - .blendEnable = info.blending.blend_enable.Value(), + .blendEnable = info.blending.blend_enable, .srcColorBlendFactor = PicaToVK::BlendFunc(info.blending.src_color_blend_factor), .dstColorBlendFactor = PicaToVK::BlendFunc(info.blending.dst_color_blend_factor), .colorBlendOp = PicaToVK::BlendEquation(info.blending.color_blend_eq), @@ -181,8 +213,8 @@ bool PipelineCache::GraphicsPipeline::Build(bool fail_on_compile_required) { }; const vk::PipelineColorBlendStateCreateInfo color_blending = { - .logicOpEnable = !info.blending.blend_enable.Value() && !instance.NeedsLogicOpEmulation(), - .logicOp = PicaToVK::LogicOp(info.blending.logic_op.Value()), + .logicOpEnable = !info.blending.blend_enable && !instance.NeedsLogicOpEmulation(), + .logicOp = PicaToVK::LogicOp(info.blending.logic_op), .attachmentCount = 1, .pAttachments = &colorblend_attachment, .blendConstants = std::array{1.0f, 1.0f, 1.0f, 1.0f}, @@ -209,27 +241,39 @@ bool PipelineCache::GraphicsPipeline::Build(bool fail_on_compile_required) { .pScissors = &scissor, }; - const bool extended_dynamic_states = instance.IsExtendedDynamicStateSupported(); - const std::array dynamic_states = { - vk::DynamicState::eViewport, - vk::DynamicState::eScissor, - vk::DynamicState::eStencilCompareMask, - vk::DynamicState::eStencilWriteMask, - vk::DynamicState::eStencilReference, - vk::DynamicState::eBlendConstants, - // VK_EXT_extended_dynamic_state - vk::DynamicState::eCullModeEXT, - vk::DynamicState::eDepthCompareOpEXT, - vk::DynamicState::eDepthTestEnableEXT, - vk::DynamicState::eDepthWriteEnableEXT, - vk::DynamicState::eFrontFaceEXT, - vk::DynamicState::ePrimitiveTopologyEXT, - vk::DynamicState::eStencilOpEXT, - vk::DynamicState::eStencilTestEnableEXT, + boost::container::static_vector dynamic_states = { + vk::DynamicState::eViewport, vk::DynamicState::eScissor, + vk::DynamicState::eStencilCompareMask, vk::DynamicState::eStencilWriteMask, + vk::DynamicState::eStencilReference, vk::DynamicState::eBlendConstants, }; + if (instance.IsExtendedDynamicStateSupported()) { + constexpr std::array extended = { + vk::DynamicState::eCullModeEXT, vk::DynamicState::eDepthCompareOpEXT, + vk::DynamicState::eDepthTestEnableEXT, vk::DynamicState::eDepthWriteEnableEXT, + vk::DynamicState::eFrontFaceEXT, vk::DynamicState::ePrimitiveTopologyEXT, + vk::DynamicState::eStencilOpEXT, vk::DynamicState::eStencilTestEnableEXT, + }; + dynamic_states.insert(dynamic_states.end(), extended.begin(), extended.end()); + } + if (instance.IsExtendedDynamicState2Supported()) { + dynamic_states.push_back(vk::DynamicState::eLogicOpEXT); + } + if (instance.IsExtendedDynamicState3LogicOpSupported()) { + dynamic_states.push_back(vk::DynamicState::eLogicOpEnableEXT); + } + if (instance.IsExtendedDynamicState3BlendEnableSupported()) { + dynamic_states.push_back(vk::DynamicState::eColorBlendEnableEXT); + } + if (instance.IsExtendedDynamicState3BlendEqSupported()) { + dynamic_states.push_back(vk::DynamicState::eColorBlendEquationEXT); + } + if (instance.IsExtendedDynamicState3ColorMaskSupported()) { + dynamic_states.push_back(vk::DynamicState::eColorWriteMaskEXT); + } + const vk::PipelineDynamicStateCreateInfo dynamic_info = { - .dynamicStateCount = extended_dynamic_states ? static_cast(dynamic_states.size()) : 6u, + .dynamicStateCount = static_cast(dynamic_states.size()), .pDynamicStates = dynamic_states.data(), }; @@ -393,17 +437,14 @@ void PipelineCache::SaveDiskCache() { MICROPROFILE_DEFINE(Vulkan_Bind, "Vulkan", "Pipeline Bind", MP_RGB(192, 32, 32)); bool PipelineCache::BindPipeline(const PipelineInfo& info, bool wait_built) { MICROPROFILE_SCOPE(Vulkan_Bind); - std::size_t shader_hash = 0; + + u64 shader_hash = 0; for (u32 i = 0; i < MAX_SHADER_STAGES; i++) { shader_hash = Common::HashCombine(shader_hash, shader_hashes[i]); } - const u64 info_hash_size = instance.IsExtendedDynamicStateSupported() - ? offsetof(PipelineInfo, rasterization) - : offsetof(PipelineInfo, dynamic); - - u64 info_hash = Common::ComputeHash64(&info, info_hash_size); - u64 pipeline_hash = Common::HashCombine(shader_hash, info_hash); + const u64 info_hash = info.Hash(instance); + const u64 pipeline_hash = Common::HashCombine(shader_hash, info_hash); auto [it, new_pipeline] = graphics_pipelines.try_emplace(pipeline_hash); if (new_pipeline) { @@ -611,12 +652,6 @@ void PipelineCache::SetScissor(s32 x, s32 y, u32 width, u32 height) { } void PipelineCache::ApplyDynamic(const PipelineInfo& info, bool is_dirty) { - if (!is_dirty && info.dynamic == current_info.dynamic && - info.rasterization.value == current_info.rasterization.value && - info.depth_stencil.value == current_info.depth_stencil.value) { - return; - } - scheduler.Record([is_dirty, current_dynamic = current_info.dynamic, dynamic = info.dynamic](vk::CommandBuffer cmdbuf) { if (dynamic.stencil_compare_mask != current_dynamic.stencil_compare_mask || is_dirty) { @@ -689,6 +724,61 @@ void PipelineCache::ApplyDynamic(const PipelineInfo& info, bool is_dirty) { }); } + if (instance.IsExtendedDynamicState2Supported()) { + scheduler.Record( + [is_dirty, logic_op = info.blending.logic_op, + current_logic_op = current_info.blending.logic_op](vk::CommandBuffer cmdbuf) { + if (logic_op != current_logic_op || is_dirty) { + cmdbuf.setLogicOpEXT(PicaToVK::LogicOp(logic_op)); + } + }); + } + + if (instance.IsExtendedDynamicState3LogicOpSupported() && !instance.NeedsLogicOpEmulation()) { + scheduler.Record( + [is_dirty, blend_enable = info.blending.blend_enable, + current_blend_enable = current_info.blending.blend_enable](vk::CommandBuffer cmdbuf) { + if (blend_enable != current_blend_enable || is_dirty) { + cmdbuf.setLogicOpEnableEXT(!blend_enable); + } + }); + } + if (instance.IsExtendedDynamicState3BlendEnableSupported()) { + scheduler.Record( + [is_dirty, blend_enable = info.blending.blend_enable, + current_blend_enable = current_info.blending.blend_enable](vk::CommandBuffer cmdbuf) { + if (blend_enable != current_blend_enable || is_dirty) { + cmdbuf.setColorBlendEnableEXT(0, blend_enable); + } + }); + } + if (instance.IsExtendedDynamicState3BlendEqSupported()) { + scheduler.Record([is_dirty, blending = info.blending, + current_blending = current_info.blending](vk::CommandBuffer cmdbuf) { + if (blending.value != current_blending.value || is_dirty) { + const vk::ColorBlendEquationEXT blend_info = { + .srcColorBlendFactor = PicaToVK::BlendFunc(blending.src_color_blend_factor), + .dstColorBlendFactor = PicaToVK::BlendFunc(blending.dst_color_blend_factor), + .colorBlendOp = PicaToVK::BlendEquation(blending.color_blend_eq), + .srcAlphaBlendFactor = PicaToVK::BlendFunc(blending.src_alpha_blend_factor), + .dstAlphaBlendFactor = PicaToVK::BlendFunc(blending.dst_alpha_blend_factor), + .alphaBlendOp = PicaToVK::BlendEquation(blending.alpha_blend_eq), + }; + + cmdbuf.setColorBlendEquationEXT(0, blend_info); + } + }); + } + if (instance.IsExtendedDynamicState3ColorMaskSupported()) { + scheduler.Record([is_dirty, color_mask = info.blending.color_write_mask, + current_color_mask = + current_info.blending.color_write_mask](vk::CommandBuffer cmdbuf) { + if (color_mask != current_color_mask || is_dirty) { + cmdbuf.setColorWriteMaskEXT(0, static_cast(color_mask)); + } + }); + } + current_info = info; } diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index dd75fceff..8cdfff01e 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h @@ -45,17 +45,19 @@ union DepthStencilState { BitField<15, 3, Pica::FramebufferRegs::CompareFunc> stencil_compare_op; }; -union BlendingState { - u32 value = 0; - BitField<0, 1, u32> blend_enable; - BitField<1, 4, Pica::FramebufferRegs::BlendFactor> src_color_blend_factor; - BitField<5, 4, Pica::FramebufferRegs::BlendFactor> dst_color_blend_factor; - BitField<9, 3, Pica::FramebufferRegs::BlendEquation> color_blend_eq; - BitField<12, 4, Pica::FramebufferRegs::BlendFactor> src_alpha_blend_factor; - BitField<16, 4, Pica::FramebufferRegs::BlendFactor> dst_alpha_blend_factor; - BitField<20, 3, Pica::FramebufferRegs::BlendEquation> alpha_blend_eq; - BitField<23, 4, u32> color_write_mask; - BitField<27, 4, Pica::FramebufferRegs::LogicOp> logic_op; +struct BlendingState { + u16 blend_enable; + u16 color_write_mask; + Pica::FramebufferRegs::LogicOp logic_op; + union { + u32 value = 0; + BitField<0, 4, Pica::FramebufferRegs::BlendFactor> src_color_blend_factor; + BitField<4, 4, Pica::FramebufferRegs::BlendFactor> dst_color_blend_factor; + BitField<8, 3, Pica::FramebufferRegs::BlendEquation> color_blend_eq; + BitField<11, 4, Pica::FramebufferRegs::BlendFactor> src_alpha_blend_factor; + BitField<15, 4, Pica::FramebufferRegs::BlendFactor> dst_alpha_blend_factor; + BitField<19, 3, Pica::FramebufferRegs::BlendEquation> alpha_blend_eq; + }; }; struct DynamicState { @@ -90,20 +92,27 @@ struct VertexLayout { std::array attributes; }; +struct AttachmentInfo { + VideoCore::PixelFormat color_format; + VideoCore::PixelFormat depth_format; +}; + /** * Information about a graphics/compute pipeline */ struct PipelineInfo { VertexLayout vertex_layout{}; BlendingState blending{}; - VideoCore::PixelFormat color_attachment = VideoCore::PixelFormat::RGBA8; - VideoCore::PixelFormat depth_attachment = VideoCore::PixelFormat::D24S8; + AttachmentInfo attachments{}; RasterizationState rasterization{}; DepthStencilState depth_stencil{}; DynamicState dynamic; + /// Returns the hash of the info structure + u64 Hash(const Instance& instance) const; + [[nodiscard]] bool IsDepthWriteEnabled() const noexcept { - const bool has_stencil = depth_attachment == VideoCore::PixelFormat::D24S8; + const bool has_stencil = attachments.depth_format == VideoCore::PixelFormat::D24S8; const bool depth_write = depth_stencil.depth_test_enable && depth_stencil.depth_write_enable; const bool stencil_write = diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 6edb572cd..8c7364378 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -467,7 +467,7 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) { const bool shadow_rendering = regs.framebuffer.IsShadowRendering(); const bool has_stencil = regs.framebuffer.HasStencil(); - const bool write_color_fb = shadow_rendering || pipeline_info.blending.color_write_mask.Value(); + const bool write_color_fb = shadow_rendering || pipeline_info.blending.color_write_mask; const bool write_depth_fb = pipeline_info.IsDepthWriteEnabled(); const bool using_color_fb = regs.framebuffer.framebuffer.GetColorBufferPhysicalAddress() != 0 && write_color_fb; @@ -485,9 +485,9 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) { return true; } - pipeline_info.color_attachment = + pipeline_info.attachments.color_format = color_surface ? color_surface->pixel_format : VideoCore::PixelFormat::Invalid; - pipeline_info.depth_attachment = + pipeline_info.attachments.depth_format = depth_surface ? depth_surface->pixel_format : VideoCore::PixelFormat::Invalid; const u16 res_scale = color_surface != nullptr @@ -693,8 +693,8 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) { const FramebufferInfo framebuffer_info = { .color = color_surface ? color_surface->GetFramebufferView() : VK_NULL_HANDLE, .depth = depth_surface ? depth_surface->GetFramebufferView() : VK_NULL_HANDLE, - .renderpass = renderpass_cache.GetRenderpass(pipeline_info.color_attachment, - pipeline_info.depth_attachment, false), + .renderpass = renderpass_cache.GetRenderpass(pipeline_info.attachments.color_format, + pipeline_info.attachments.depth_format, false), .width = width, .height = height, }; @@ -1149,8 +1149,8 @@ void RasterizerVulkan::SyncCullMode() { } void RasterizerVulkan::SyncBlendEnabled() { - pipeline_info.blending.blend_enable.Assign( - Pica::g_state.regs.framebuffer.output_merger.alphablend_enable); + pipeline_info.blending.blend_enable = + Pica::g_state.regs.framebuffer.output_merger.alphablend_enable; } void RasterizerVulkan::SyncBlendFuncs() { @@ -1182,7 +1182,7 @@ void RasterizerVulkan::SyncLogicOp() { } const auto& regs = Pica::g_state.regs; - pipeline_info.blending.logic_op.Assign(regs.framebuffer.output_merger.logic_op); + pipeline_info.blending.logic_op = regs.framebuffer.output_merger.logic_op; const bool is_logic_op_emulated = instance.NeedsLogicOpEmulation() && !regs.framebuffer.output_merger.alphablend_enable; @@ -1191,7 +1191,7 @@ void RasterizerVulkan::SyncLogicOp() { if (is_logic_op_emulated && is_logic_op_noop) { // Color output is disabled by logic operation. We use color write mask to skip // color but allow depth write. - pipeline_info.blending.color_write_mask.Assign(0); + pipeline_info.blending.color_write_mask = 0; } } @@ -1211,7 +1211,7 @@ void RasterizerVulkan::SyncColorWriteMask() { return; } - pipeline_info.blending.color_write_mask.Assign(color_mask); + pipeline_info.blending.color_write_mask = color_mask; } void RasterizerVulkan::SyncStencilWriteMask() {