From 30d57bed0699430f4571e8967a75b265287437ac Mon Sep 17 00:00:00 2001 From: nmzik Date: Sat, 15 Aug 2026 08:13:30 +0200 Subject: [PATCH] graphics: honor primitive restart state --- .../command_processor/pm4Handlers.cpp | 16 ++---- src/graphics/guest_gpu/hardwareContext.h | 18 ++++--- src/graphics/host_gpu/renderer/debug.cpp | 11 ++-- .../renderer/pipeline/pipelineCache.cpp | 14 ++--- .../renderer/pipeline/pipelineCache.h | 11 ++-- .../host_gpu/renderer/pipeline/shaders.cpp | 3 +- src/graphics/host_gpu/renderer/render.h | 11 ++-- src/graphics/host_gpu/renderer/renderDraw.cpp | 51 ++++++++++++++++--- 8 files changed, 90 insertions(+), 45 deletions(-) diff --git a/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp b/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp index d6bab6b6b..ebbdf4ff4 100644 --- a/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp +++ b/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp @@ -1627,16 +1627,10 @@ KYTY_HW_UC_PARSER(HwUcSetIaMultiVgtParam) { return num_values; } -static void HwIgnoreMultiPrimIbReset([[maybe_unused]] uint32_t value) {} - KYTY_HW_UC_PARSER(HwUcSetMultiPrimIbReset) { - auto num_values = KYTY_PM4_LEN(cmd_id) - 2u; - - for (uint32_t i = 0; i < num_values; i++) { - HwIgnoreMultiPrimIbReset(buffer[i]); - } - - return num_values; + EXIT_NOT_IMPLEMENTED(cmd_offset != Pm4::GE_MULTI_PRIM_IB_RESET_EN); + cp.GetUcfg().SetPrimitiveResetControl(buffer[0]); + return 1; } static void HwUcIgnoreBorderColorTableAddr([[maybe_unused]] uint32_t cmd_offset, @@ -3546,7 +3540,7 @@ void GraphicsInitJmpTablesCxIndirect() { cp.GetCtx().SetReuseOff(value); }; g_hw_ctx_indirect_func[Pm4::VGT_MULTI_PRIM_IB_RESET_INDX] = [](KYTY_HW_CTX_INDIRECT_ARGS) { - HwIgnoreMultiPrimIbReset(value); + cp.GetCtx().SetPrimitiveResetIndex(value); }; g_hw_ctx_indirect_func[Pm4::VGT_TESS_DISTRIBUTION] = [](KYTY_HW_CTX_INDIRECT_ARGS) { cp.GetCtx().SetTessDistribution(value); @@ -4424,7 +4418,7 @@ void GraphicsInitJmpTablesUcIndirect() { HwUcIgnoreIaMultiVgtParam(value); }; g_hw_uc_indirect_func[Pm4::GE_MULTI_PRIM_IB_RESET_EN] = [](KYTY_HW_UC_INDIRECT_ARGS) { - HwIgnoreMultiPrimIbReset(value); + cp.GetUcfg().SetPrimitiveResetControl(value); }; g_hw_uc_indirect_func[Pm4::TA_CS_BC_BASE_ADDR] = [](KYTY_HW_UC_INDIRECT_ARGS) { HwUcIgnoreBorderColorTableAddr(cmd_offset, value); diff --git a/src/graphics/guest_gpu/hardwareContext.h b/src/graphics/guest_gpu/hardwareContext.h index d9f8f9d18..2cb5997a9 100644 --- a/src/graphics/guest_gpu/hardwareContext.h +++ b/src/graphics/guest_gpu/hardwareContext.h @@ -941,8 +941,10 @@ public: [[nodiscard]] uint8_t GetStencilClearValue() const { return m_stencil_clear_value; } void SetStencilClearValue(uint8_t clear_value) { m_stencil_clear_value = clear_value; } - [[nodiscard]] float GetLineWidth() const { return m_line_width; } - void SetLineWidth(float width) { m_line_width = width; } + [[nodiscard]] float GetLineWidth() const { return m_line_width; } + void SetLineWidth(float width) { m_line_width = width; } + [[nodiscard]] uint32_t GetPrimitiveResetIndex() const { return m_primitive_reset_index; } + void SetPrimitiveResetIndex(uint32_t index) { m_primitive_reset_index = index; } [[nodiscard]] const ShaderRegisters& GetShaderRegisters() const { return m_sh_regs; } @@ -987,7 +989,8 @@ public: void SetScShaderControl(uint32_t value) { m_sh_regs.m_paScShaderControl = value; } private: - float m_line_width = 1.0f; + float m_line_width = 1.0f; + uint32_t m_primitive_reset_index = 0xffffffffu; BlendControl m_blend_control[8]; BlendColor m_blend_color; @@ -1035,6 +1038,8 @@ public: [[nodiscard]] uint32_t GetIndexOffset() const { return m_index_offset; } void SetObjectId(uint32_t object_id) { m_object_id = object_id; } [[nodiscard]] uint32_t GetObjectId() const { return m_object_id; } + void SetPrimitiveResetControl(uint32_t control) { m_primitive_reset_control = control; } + [[nodiscard]] uint32_t GetPrimitiveResetControl() const { return m_primitive_reset_control; } [[nodiscard]] const GeControl& GetGeControl() const { return m_ge_cntl; } void SetGeControl(const GeControl& control) { m_ge_cntl = control; } @@ -1049,9 +1054,10 @@ public: void SetGdsOaAddress(uint32_t value) { m_gds_oa.counters[m_gds_oa.GetIndex()].address = value; } private: - Prospero::PrimitiveType m_prim_type = Prospero::PrimitiveType::kNone; - uint32_t m_index_offset = 0; - uint32_t m_object_id = 0; + Prospero::PrimitiveType m_prim_type = Prospero::PrimitiveType::kNone; + uint32_t m_index_offset = 0; + uint32_t m_object_id = 0; + uint32_t m_primitive_reset_control = 0; GeControl m_ge_cntl; GeUserVgprEn m_ge_user_vgpr_en; diff --git a/src/graphics/host_gpu/renderer/debug.cpp b/src/graphics/host_gpu/renderer/debug.cpp index cf2c83cf3..ec16789ae 100644 --- a/src/graphics/host_gpu/renderer/debug.cpp +++ b/src/graphics/host_gpu/renderer/debug.cpp @@ -71,14 +71,16 @@ void uc_print(const char* func, const HW::UserConfig& uc) { LOGF("\t GetPrimType() = 0x%08" PRIx32 "\n" "\t GetIndexOffset() = 0x%08" PRIx32 "\n" "\t GetObjectId() = 0x%08" PRIx32 "\n" + "\t primitive_reset = 0x%08" PRIx32 "\n" "\t primitive_group_size = 0x%04" PRIx16 "\n" "\t vertex_group_size = 0x%04" PRIx16 "\n" "\t en_user_vgpr1 = %s\n" "\t en_user_vgpr2 = %s\n" "\t en_user_vgpr3 = %s\n", static_cast(uc.GetPrimType()), uc.GetIndexOffset(), uc.GetObjectId(), - ge_cntl.primitive_group_size, ge_cntl.vertex_group_size, user_en.vgpr1 ? "true" : "false", - user_en.vgpr2 ? "true" : "false", user_en.vgpr3 ? "true" : "false"); + uc.GetPrimitiveResetControl(), ge_cntl.primitive_group_size, ge_cntl.vertex_group_size, + user_en.vgpr1 ? "true" : "false", user_en.vgpr2 ? "true" : "false", + user_en.vgpr3 ? "true" : "false"); } void uc_check(const HW::UserConfig& uc) { @@ -1265,9 +1267,10 @@ void hw_print(const RenderCommandBuffer& buffer) { "\t GetRenderTargetMask() = 0x%08" PRIx32 "\n" "\t GetDepthClearValue() = %f\n" "\t GetStencilClearValue() = %" PRIu8 "\n" - "\t GetLineWidth() = %f\n", + "\t GetLineWidth() = %f\n" + "\t primitive_reset_index = 0x%08" PRIx32 "\n", hw.GetRenderTargetMask(), hw.GetDepthClearValue(), hw.GetStencilClearValue(), - hw.GetLineWidth()); + hw.GetLineWidth(), hw.GetPrimitiveResetIndex()); LOGF("%s", Common::Concat(rt_print("RenderTraget:", rt), "").c_str()); diff --git a/src/graphics/host_gpu/renderer/pipeline/pipelineCache.cpp b/src/graphics/host_gpu/renderer/pipeline/pipelineCache.cpp index a187c3733..e3acb2bf2 100644 --- a/src/graphics/host_gpu/renderer/pipeline/pipelineCache.cpp +++ b/src/graphics/host_gpu/renderer/pipeline/pipelineCache.cpp @@ -55,8 +55,9 @@ bool PipelineStaticParameters::operator==(const PipelineStaticParameters& other) PipelineCache::GraphicsPipeline& PipelineCache::CreateGraphicsPipeline( RenderColorInfo* colors, uint32_t color_count, RenderDepthInfo& depth, ShaderVertexInputInfo& vs_input_info, RenderCommandBuffer& command, - ShaderPixelInputInfo* ps_input_info, vk::PrimitiveTopology topology, bool ps_active, - std::span vs_spirv, std::span ps_spirv) { + ShaderPixelInputInfo* ps_input_info, vk::PrimitiveTopology topology, + bool primitive_restart_enable, bool ps_active, std::span vs_spirv, + std::span ps_spirv) { KYTY_PROFILER_BLOCK("PipelineCache::CreatePipeline(Gfx)", profiler::colors::DeepOrangeA200); EXIT_IF(colors == nullptr); @@ -132,10 +133,11 @@ PipelineCache::GraphicsPipeline& PipelineCache::CreateGraphicsPipeline( const auto& clip_control = ctx.GetClipControl(); EXIT_NOT_IMPLEMENTED(!clip_control.IsZClipModeRepresentable()); - static_params.negative_one_to_one = !clip_control.dx_clip_space; - static_params.depth_clip_enable = clip_control.IsZClipEnabled(); - static_params.topology = topology; - static_params.samples = attachment_samples; + static_params.negative_one_to_one = !clip_control.dx_clip_space; + static_params.depth_clip_enable = clip_control.IsZClipEnabled(); + static_params.topology = topology; + static_params.primitive_restart_enable = primitive_restart_enable; + static_params.samples = attachment_samples; static_params.sample_shading_enable = ps_active && attachment_samples > 1 && ps_input_info->ps_sample_shading; if (static_params.sample_shading_enable && !m_graphics.sample_rate_shading_enabled) { diff --git a/src/graphics/host_gpu/renderer/pipeline/pipelineCache.h b/src/graphics/host_gpu/renderer/pipeline/pipelineCache.h index a772ad67b..e4bff1031 100644 --- a/src/graphics/host_gpu/renderer/pipeline/pipelineCache.h +++ b/src/graphics/host_gpu/renderer/pipeline/pipelineCache.h @@ -38,6 +38,7 @@ struct PipelineStaticParameters { bool depth_clip_enable = true; int scissor_ltrb[4] = {0}; vk::PrimitiveTopology topology = vk::PrimitiveTopology::ePointList; + bool primitive_restart_enable = false; uint32_t samples = 1; bool sample_shading_enable = false; bool with_depth = false; @@ -79,9 +80,9 @@ static_assert(std::is_standard_layout_v); static_assert(alignof(PipelineStaticParameters) == 1); static_assert(sizeof(PipelineStaticParameters) == sizeof(float[3]) + sizeof(float[3]) + sizeof(bool) * 2 + sizeof(int[4]) + - sizeof(vk::PrimitiveTopology) + sizeof(uint32_t) + sizeof(bool) * 4 + - sizeof(vk::CompareOp) + sizeof(bool) + sizeof(float) * 2 + sizeof(bool) + - sizeof(PipelineStencilStaticState) * 2 + sizeof(uint32_t) + + sizeof(vk::PrimitiveTopology) + sizeof(bool) + sizeof(uint32_t) + + sizeof(bool) * 4 + sizeof(vk::CompareOp) + sizeof(bool) + sizeof(float) * 2 + + sizeof(bool) + sizeof(PipelineStencilStaticState) * 2 + sizeof(uint32_t) + sizeof(uint32_t[RENDER_COLOR_ATTACHMENTS_MAX]) + sizeof(bool) * 3 + sizeof(uint8_t[RENDER_COLOR_ATTACHMENTS_MAX]) * 6 + sizeof(bool[RENDER_COLOR_ATTACHMENTS_MAX]) * 3 + sizeof(float) * 4); @@ -122,8 +123,8 @@ public: CreateGraphicsPipeline(RenderColorInfo* colors, uint32_t color_count, RenderDepthInfo& depth, ShaderVertexInputInfo& vs_input_info, RenderCommandBuffer& command, ShaderPixelInputInfo* ps_input_info, vk::PrimitiveTopology topology, - bool ps_active, std::span vs_spirv, - std::span ps_spirv); + bool primitive_restart_enable, bool ps_active, + std::span vs_spirv, std::span ps_spirv); ComputePipeline& CreateComputePipeline(ShaderComputeInputInfo& input_info, const HW::ComputeShaderInfo& cs_regs, std::span cs_spirv); diff --git a/src/graphics/host_gpu/renderer/pipeline/shaders.cpp b/src/graphics/host_gpu/renderer/pipeline/shaders.cpp index e9c5bfed8..7f05fa027 100644 --- a/src/graphics/host_gpu/renderer/pipeline/shaders.cpp +++ b/src/graphics/host_gpu/renderer/pipeline/shaders.cpp @@ -726,7 +726,8 @@ void CreatePipelineInternal( input_assembly.pNext = nullptr; input_assembly.flags = {}; input_assembly.topology = static_params.topology; - input_assembly.primitiveRestartEnable = VK_FALSE; + input_assembly.primitiveRestartEnable = + static_params.primitive_restart_enable ? VK_TRUE : VK_FALSE; vk::Viewport viewport {}; viewport.x = static_params.viewport_offset[0] - static_params.viewport_scale[0]; diff --git a/src/graphics/host_gpu/renderer/render.h b/src/graphics/host_gpu/renderer/render.h index 5c5bd3115..d2ef08e1c 100644 --- a/src/graphics/host_gpu/renderer/render.h +++ b/src/graphics/host_gpu/renderer/render.h @@ -212,11 +212,12 @@ private: const DrawCallInfo& draw, uint32_t render_target_slice_offset, bool log_setup_phases, DrawRenderState& state); - void ExecutePreparedDraw(uint64_t submit_id, RenderCommandBuffer& buffer, - const DrawCallInfo& draw, DrawRenderState& state, - vk::PrimitiveTopology topology, const DrawEmitInfo& emit, - const DrawIndexBufferSource& index_source, bool log_pipeline_phase, - bool set_bind_debug, bool set_auto_debug); + void ExecutePreparedDraw(uint64_t submit_id, RenderCommandBuffer& buffer, + const DrawCallInfo& draw, DrawRenderState& state, + vk::PrimitiveTopology topology, const DrawEmitInfo& emit, + const DrawIndexBufferSource& index_source, + bool primitive_restart_enable, bool log_pipeline_phase, + bool set_bind_debug, bool set_auto_debug); [[nodiscard]] RenderState AcquireRenderTargets(CommandBuffer& buffer, RenderColorInfo* colors, uint32_t color_count, RenderDepthInfo& depth); [[nodiscard]] bool ResolveColorTargets(uint64_t submit_id, RenderCommandBuffer& buffer, diff --git a/src/graphics/host_gpu/renderer/renderDraw.cpp b/src/graphics/host_gpu/renderer/renderDraw.cpp index cdb9cc010..d30c7124a 100644 --- a/src/graphics/host_gpu/renderer/renderDraw.cpp +++ b/src/graphics/host_gpu/renderer/renderDraw.cpp @@ -881,6 +881,41 @@ static bool GetDrawTopology(const HW::UserConfig& ucfg, bool auto_draw, return true; } +static bool ResolvePrimitiveRestart(const RenderCommandBuffer& buffer, + vk::PrimitiveTopology topology, uint32_t index_type_and_size) { + const auto control = buffer.GetUserConfig().GetPrimitiveResetControl(); + EXIT_NOT_IMPLEMENTED((control & ~0x3u) != 0); + if ((control & 0x1u) == 0) { + return false; + } + switch (buffer.GetUserConfig().GetPrimType()) { + case Prospero::PrimitiveType::kLineStrip: + case Prospero::PrimitiveType::kTriFan: + case Prospero::PrimitiveType::kTriStrip: break; + default: return false; + } + if (topology != vk::PrimitiveTopology::eLineStrip && + topology != vk::PrimitiveTopology::eTriangleStrip && + topology != vk::PrimitiveTopology::eTriangleFan) { + return false; + } + + uint32_t index_mask = 0; + switch (static_cast(index_type_and_size)) { + case Prospero::IndexType::kIndex8: index_mask = 0xffu; break; + case Prospero::IndexType::kIndex16: index_mask = 0xffffu; break; + case Prospero::IndexType::kIndex32: index_mask = 0xffffffffu; break; + default: EXIT("unknown index_type_and_size: %u\n", index_type_and_size); + } + + const auto reset_index = buffer.GetRegisters().GetPrimitiveResetIndex(); + if ((control & 0x2u) != 0 && (reset_index & ~index_mask) != 0) { + return false; + } + EXIT_NOT_IMPLEMENTED((reset_index & index_mask) != index_mask); + return true; +} + bool RenderExecutor::PrepareDrawRenderState(uint64_t submit_id, RenderCommandBuffer& buffer, const DrawCallInfo& draw, uint32_t render_target_slice_offset, @@ -1096,8 +1131,8 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, RenderCommandBuffer const DrawCallInfo& draw, DrawRenderState& state, vk::PrimitiveTopology topology, const DrawEmitInfo& emit, const DrawIndexBufferSource& index_source, - bool log_pipeline_phase, bool set_bind_debug, - bool set_auto_debug) { + bool primitive_restart_enable, bool log_pipeline_phase, + bool set_bind_debug, bool set_auto_debug) { EXIT_IF(draw.name == nullptr); auto& ucfg = buffer.GetUserConfig(); @@ -1114,7 +1149,8 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, RenderCommandBuffer } auto& pipeline = m_context.GetPipelineCache().CreateGraphicsPipeline( state.color_info, state.color_count, state.depth_info, state.vs_input_info, buffer, - &state.ps_input_info, topology, state.ps_active, state.vs_shader, state.ps_shader); + &state.ps_input_info, topology, primitive_restart_enable, state.ps_active, state.vs_shader, + state.ps_shader); // Resource preparation above may synchronously finish and restart the scheduler. From this // point onward, every operation targets the current command buffer and cannot touch guest @@ -1238,6 +1274,7 @@ void RenderExecutor::DrawIndex(uint64_t submit_id, RenderCommandBuffer& buffer, vk::IndexType index_type = vk::IndexType::eUint16; uint64_t index_size = 0; bool expand_index8_to_u16 = false; + const bool primitive_restart = ResolvePrimitiveRestart(buffer, topology, index_type_and_size); switch (static_cast(index_type_and_size)) { case Prospero::IndexType::kIndex16: @@ -1268,7 +1305,7 @@ void RenderExecutor::DrawIndex(uint64_t submit_id, RenderCommandBuffer& buffer, const auto* src = static_cast(index_addr); expanded_indices.resize(index_count); for (uint32_t i = 0; i < index_count; i++) { - expanded_indices[i] = src[i]; + expanded_indices[i] = primitive_restart && src[i] == 0xffu ? 0xffffu : src[i]; } } @@ -1298,8 +1335,8 @@ void RenderExecutor::DrawIndex(uint64_t submit_id, RenderCommandBuffer& buffer, emit.indexed = true; emit.vertex_offset = vertex_offset; - ExecutePreparedDraw(submit_id, buffer, draw, state, topology, emit, index_source, true, true, - false); + ExecutePreparedDraw(submit_id, buffer, draw, state, topology, emit, index_source, + primitive_restart, true, true, false); ResetBindings(); } @@ -1398,7 +1435,7 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, RenderCommandBuffer& buffer, u DrawIndexBufferSource index_source {}; ExecutePreparedDraw(submit_id, buffer, draw, state, topology, emit, index_source, false, false, - true); + false, true); ResetBindings(); }