diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 55a0e5a7e..438fa031e 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -982,12 +982,6 @@ void RendererVulkan::FlushBuffers() { } void RendererVulkan::OnSlotSwitch() { - // When the command buffer switches, all state becomes undefined. - // This is problematic with dynamic states, so set all states here - if (instance.IsExtendedDynamicStateSupported()) { - rasterizer->SyncFixedState(); - } - runtime.OnSlotSwitch(scheduler.GetCurrentSlotIndex()); renderpass_cache.OnSlotSwitch(); rasterizer->pipeline_cache.MarkDirty(); diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 1f529644d..1a4d458a9 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -75,6 +75,8 @@ u32 AttribBytes(VertexAttribute attrib) { case AttribType::Ubyte: return sizeof(u8) * attrib.size; } + + return 0; } vk::Format ToVkAttributeFormat(VertexAttribute attrib) { @@ -264,26 +266,105 @@ void PipelineCache::BindSampler(u32 binding, vk::Sampler sampler) { } void PipelineCache::SetViewport(float x, float y, float width, float height) { - vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); - command_buffer.setViewport(0, vk::Viewport{x, y, width, height, 0.f, 1.f}); + const vk::Viewport viewport{x, y, width, height, 0.f, 1.f}; + + if (viewport != current_viewport || state_dirty) { + vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); + command_buffer.setViewport(0, vk::Viewport{x, y, width, height, 0.f, 1.f}); + current_viewport = viewport; + } } void PipelineCache::SetScissor(s32 x, s32 y, u32 width, u32 height) { - vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); - command_buffer.setScissor(0, vk::Rect2D{{x, y}, {width, height}}); + const vk::Rect2D scissor{{x, y}, {width, height}}; + + if (scissor != current_scissor || state_dirty) { + vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); + command_buffer.setScissor(0, vk::Rect2D{{x, y}, {width, height}}); + current_scissor = scissor; + } } void PipelineCache::MarkDirty() { descriptor_dirty.fill(true); current_pipeline = VK_NULL_HANDLE; + state_dirty = true; } void PipelineCache::ApplyDynamic(const PipelineInfo& info) { - if (instance.IsExtendedDynamicStateSupported()) { - vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); - command_buffer.setPrimitiveTopologyEXT( - PicaToVK::PrimitiveTopology(info.rasterization.topology)); + vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); + + if (info.depth_stencil.stencil_compare_mask != + current_info.depth_stencil.stencil_compare_mask || + state_dirty) { + command_buffer.setStencilCompareMask(vk::StencilFaceFlagBits::eFrontAndBack, + info.depth_stencil.stencil_compare_mask); } + + if (info.depth_stencil.stencil_write_mask != current_info.depth_stencil.stencil_write_mask || + state_dirty) { + command_buffer.setStencilWriteMask(vk::StencilFaceFlagBits::eFrontAndBack, + info.depth_stencil.stencil_write_mask); + } + + if (info.depth_stencil.stencil_reference != current_info.depth_stencil.stencil_reference || + state_dirty) { + command_buffer.setStencilReference(vk::StencilFaceFlagBits::eFrontAndBack, + info.depth_stencil.stencil_reference); + } + + if (instance.IsExtendedDynamicStateSupported()) { + if (info.rasterization.cull_mode != current_info.rasterization.cull_mode || state_dirty) { + command_buffer.setCullModeEXT(PicaToVK::CullMode(info.rasterization.cull_mode)); + command_buffer.setFrontFaceEXT(PicaToVK::FrontFace(info.rasterization.cull_mode)); + } + + if (info.depth_stencil.depth_compare_op != current_info.depth_stencil.depth_compare_op || + state_dirty) { + command_buffer.setDepthCompareOpEXT( + PicaToVK::CompareFunc(info.depth_stencil.depth_compare_op)); + } + + if (info.depth_stencil.depth_test_enable != current_info.depth_stencil.depth_test_enable || + state_dirty) { + command_buffer.setDepthTestEnableEXT(info.depth_stencil.depth_test_enable); + } + + if (info.depth_stencil.depth_write_enable != + current_info.depth_stencil.depth_write_enable || + state_dirty) { + command_buffer.setDepthWriteEnableEXT(info.depth_stencil.depth_write_enable); + } + + if (info.rasterization.topology != current_info.rasterization.topology || state_dirty) { + command_buffer.setPrimitiveTopologyEXT( + PicaToVK::PrimitiveTopology(info.rasterization.topology)); + } + + if (info.depth_stencil.stencil_test_enable != + current_info.depth_stencil.stencil_test_enable || + state_dirty) { + command_buffer.setStencilTestEnableEXT(info.depth_stencil.stencil_test_enable); + } + + if (info.depth_stencil.stencil_fail_op != current_info.depth_stencil.stencil_fail_op || + info.depth_stencil.stencil_pass_op != current_info.depth_stencil.stencil_pass_op || + info.depth_stencil.stencil_depth_fail_op != + current_info.depth_stencil.stencil_depth_fail_op || + info.depth_stencil.stencil_compare_op != + current_info.depth_stencil.stencil_compare_op || + state_dirty) { + command_buffer.setStencilOpEXT( + vk::StencilFaceFlagBits::eFrontAndBack, + PicaToVK::StencilOp(info.depth_stencil.stencil_fail_op), + PicaToVK::StencilOp(info.depth_stencil.stencil_pass_op), + PicaToVK::StencilOp(info.depth_stencil.stencil_depth_fail_op), + PicaToVK::CompareFunc(info.depth_stencil.stencil_compare_op)); + } + } + + current_info = info; + state_dirty = false; } void PipelineCache::SetBinding(u32 set, u32 binding, DescriptorData data) { @@ -539,12 +620,11 @@ void PipelineCache::LoadDiskCache() { if (cache_file.IsOpen()) { LOG_INFO(Render_Vulkan, "Loading pipeline cache"); - const u32 cache_file_size = cache_file.GetSize(); + const u64 cache_file_size = cache_file.GetSize(); cache_data.resize(cache_file_size); if (cache_file.ReadBytes(cache_data.data(), cache_file_size)) { if (!IsCacheValid(cache_data.data(), cache_file_size)) { - LOG_WARNING(Render_Vulkan, "Pipeline cache provided invalid, deleting"); - FileUtil::Delete(cache_file_path); + LOG_WARNING(Render_Vulkan, "Pipeline cache provided invalid, ignoring"); } else { cache_info.initialDataSize = cache_file_size; cache_info.pInitialData = cache_data.data(); diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index 5ff79a3bd..7287eebfa 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h @@ -230,6 +230,9 @@ private: vk::PipelineCache pipeline_cache; std::unordered_map> graphics_pipelines; vk::Pipeline current_pipeline{}; + PipelineInfo current_info{}; + vk::Viewport current_viewport{}; + vk::Rect2D current_scissor{}; // Cached layouts for the rasterizer pipelines vk::PipelineLayout layout; @@ -240,6 +243,7 @@ private: std::array update_data{}; std::array descriptor_dirty{}; std::array descriptor_sets; + bool state_dirty = true; // Bound shader modules enum ProgramType : u32 { VS = 0, GS = 2, FS = 1 }; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index edb8fd42e..d9ee8a05a 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -532,7 +532,7 @@ bool RasterizerVulkan::AccelerateDrawBatchInternal(bool is_indexed) { vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); if (is_indexed) { bool index_u16 = regs.pipeline.index_array.format != 0; - const u64 index_buffer_size = regs.pipeline.num_vertices * (index_u16 ? 2 : 1); + const u32 index_buffer_size = regs.pipeline.num_vertices * (index_u16 ? 2 : 1); if (index_buffer_size > INDEX_BUFFER_SIZE) { LOG_WARNING(Render_Vulkan, "Too large index input size {}", index_buffer_size); @@ -681,10 +681,8 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) { const auto BindSampler = [&](u32 binding, SamplerInfo& info, const Pica::TexturingRegs::TextureConfig& config) { // TODO(GPUCode): Cubemaps don't contain any mipmaps for now, so sampling from them returns - // nothing Always sample from the base level until mipmaps for texture cubes are implemented - // NOTE: There are no Vulkan filter modes that directly correspond to OpenGL minification - // filters GL_LINEAR/GL_NEAREST so emulate them by setting minLod = 0, and maxLod = 0.25, - // and using minFilter = VK_FILTER_LINEAR or minFilter = VK_FILTER_NEAREST + // nothing. Always sample from the base level until mipmaps for texture cubes are + // implemented const bool skip_mipmap = config.type == Pica::TexturingRegs::TextureConfig::TextureCube; info = SamplerInfo{.mag_filter = config.mag_filter, .min_filter = config.min_filter, @@ -783,7 +781,6 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) { // the geometry in question. // For example: a bug in Pokemon X/Y causes NULL-texture squares to be drawn // on the male character's face, which in the OpenGL default appear black. - // state.texture_units[texture_index].texture_2d = default_texture; pipeline_cache.BindTexture(texture_index, default_texture.image_view); } } else { @@ -1725,12 +1722,6 @@ void RasterizerVulkan::SyncClipCoef() { void RasterizerVulkan::SyncCullMode() { const auto& regs = Pica::g_state.regs; - if (instance.IsExtendedDynamicStateSupported()) { - vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); - command_buffer.setCullModeEXT(PicaToVK::CullMode(regs.rasterizer.cull_mode)); - command_buffer.setFrontFaceEXT(PicaToVK::FrontFace(regs.rasterizer.cull_mode)); - } - pipeline_info.rasterization.cull_mode.Assign(regs.rasterizer.cull_mode); } @@ -1846,10 +1837,6 @@ void RasterizerVulkan::SyncStencilWriteMask() { (regs.framebuffer.framebuffer.allow_depth_stencil_write != 0) ? static_cast(regs.framebuffer.output_merger.stencil_test.write_mask) : 0; - - vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); - command_buffer.setStencilWriteMask(vk::StencilFaceFlagBits::eFrontAndBack, - pipeline_info.depth_stencil.stencil_write_mask); } void RasterizerVulkan::SyncDepthWriteMask() { @@ -1857,12 +1844,6 @@ void RasterizerVulkan::SyncDepthWriteMask() { const bool write_enable = (regs.framebuffer.framebuffer.allow_depth_stencil_write != 0 && regs.framebuffer.output_merger.depth_write_enable); - - if (instance.IsExtendedDynamicStateSupported()) { - vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); - command_buffer.setDepthWriteEnableEXT(write_enable); - } - pipeline_info.depth_stencil.depth_write_enable.Assign(write_enable); } @@ -1873,21 +1854,6 @@ void RasterizerVulkan::SyncStencilTest() { const bool test_enable = stencil_test.enable && regs.framebuffer.framebuffer.depth_format == Pica::FramebufferRegs::DepthFormat::D24S8; - vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); - command_buffer.setStencilCompareMask(vk::StencilFaceFlagBits::eFrontAndBack, - stencil_test.input_mask); - command_buffer.setStencilReference(vk::StencilFaceFlagBits::eFrontAndBack, - stencil_test.reference_value); - - if (instance.IsExtendedDynamicStateSupported()) { - command_buffer.setStencilTestEnableEXT(test_enable); - command_buffer.setStencilOpEXT(vk::StencilFaceFlagBits::eFrontAndBack, - PicaToVK::StencilOp(stencil_test.action_stencil_fail), - PicaToVK::StencilOp(stencil_test.action_depth_pass), - PicaToVK::StencilOp(stencil_test.action_depth_fail), - PicaToVK::CompareFunc(stencil_test.func)); - } - pipeline_info.depth_stencil.stencil_test_enable.Assign(test_enable); pipeline_info.depth_stencil.stencil_fail_op.Assign(stencil_test.action_stencil_fail); pipeline_info.depth_stencil.stencil_pass_op.Assign(stencil_test.action_depth_pass); @@ -1906,12 +1872,6 @@ void RasterizerVulkan::SyncDepthTest() { ? regs.framebuffer.output_merger.depth_test_func.Value() : Pica::FramebufferRegs::CompareFunc::Always; - if (instance.IsExtendedDynamicStateSupported()) { - vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); - command_buffer.setDepthCompareOpEXT(PicaToVK::CompareFunc(compare_op)); - command_buffer.setDepthTestEnableEXT(test_enabled); - } - pipeline_info.depth_stencil.depth_test_enable.Assign(test_enabled); pipeline_info.depth_stencil.depth_compare_op.Assign(compare_op); } @@ -1991,7 +1951,7 @@ void RasterizerVulkan::SyncLightPosition(int light_index) { void RasterizerVulkan::SyncLightSpotDirection(int light_index) { const auto& light = Pica::g_state.regs.lighting.light[light_index]; - const auto spot_direction = Common::Vec3f{light.spot_x, light.spot_y, light.spot_z} / 2047.0f; + const auto spot_direction = Common::Vec3i{light.spot_x, light.spot_y, light.spot_z} / 2047.0f; if (spot_direction != uniform_block_data.data.light_src[light_index].spot_direction) { uniform_block_data.data.light_src[light_index].spot_direction = spot_direction;