diff --git a/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp b/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp index 2ad17e6f6..f47527d90 100644 --- a/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp +++ b/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp @@ -18,7 +18,9 @@ #include #include #include +#include #include +#include #include #define KYTY_HW_CTX_PARSER_ARGS \ @@ -895,23 +897,44 @@ KYTY_HW_CTX_PARSER(HwCtxSetModeControl) { return 1; } -static void HwCtxIgnorePolyOffsetRegister(uint32_t cmd_offset, uint32_t value) { - static std::atomic log_count = 0; +static void HwCtxSetPolyOffsetRegister(CommandProcessor& cp, uint32_t cmd_offset, uint32_t value) { + HW::PolyOffset offset = cp.GetCtx().GetPolyOffset(); - auto count = log_count.fetch_add(1); - if (count < 8) { - LOGF_COLOR(Log::Color::Red, - "\t temporary: ignoring polygon offset context register 0x%03" PRIx32 - " = 0x%08" PRIx32 " (depth bias not implemented)\n", - cmd_offset, value); + switch (cmd_offset) { + case Pm4::PA_SU_POLY_OFFSET_DB_FMT_CNTL: { + const auto neg_bits = + KYTY_PM4_GET(value, PA_SU_POLY_OFFSET_DB_FMT_CNTL, NEG_NUM_DB_BITS); + offset.neg_num_db_bits = std::bit_cast(static_cast(neg_bits)); + offset.db_is_float_fmt = + KYTY_PM4_GET(value, PA_SU_POLY_OFFSET_DB_FMT_CNTL, DB_IS_FLOAT_FMT) != 0; + break; + } + case Pm4::PA_SU_POLY_OFFSET_CLAMP: offset.clamp = std::bit_cast(value); break; + case Pm4::PA_SU_POLY_OFFSET_FRONT_SCALE: + offset.front_scale = std::bit_cast(value); + break; + case Pm4::PA_SU_POLY_OFFSET_FRONT_OFFSET: + offset.front_offset = std::bit_cast(value); + break; + case Pm4::PA_SU_POLY_OFFSET_BACK_SCALE: + offset.back_scale = std::bit_cast(value); + break; + case Pm4::PA_SU_POLY_OFFSET_BACK_OFFSET: + offset.back_offset = std::bit_cast(value); + break; + default: + EXIT("unknown polygon offset context register 0x%03" PRIx32 " = 0x%08" PRIx32 "\n", + cmd_offset, value); } + + cp.GetCtx().SetPolyOffset(offset); } KYTY_HW_CTX_PARSER(HwCtxSetPolyOffsetRegisters) { auto num_values = KYTY_PM4_LEN(cmd_id) - 2u; for (uint32_t i = 0; i < num_values; i++) { - HwCtxIgnorePolyOffsetRegister(cmd_offset + i, buffer[i]); + HwCtxSetPolyOffsetRegister(cp, cmd_offset + i, buffer[i]); } return num_values; @@ -3705,22 +3728,22 @@ void GraphicsInitJmpTablesCxIndirect() { }; g_hw_ctx_indirect_func[Pm4::PA_SU_POLY_OFFSET_DB_FMT_CNTL] = [](KYTY_HW_CTX_INDIRECT_ARGS) { - HwCtxIgnorePolyOffsetRegister(cmd_offset, value); + HwCtxSetPolyOffsetRegister(cp, cmd_offset, value); }; g_hw_ctx_indirect_func[Pm4::PA_SU_POLY_OFFSET_CLAMP] = [](KYTY_HW_CTX_INDIRECT_ARGS) { - HwCtxIgnorePolyOffsetRegister(cmd_offset, value); + HwCtxSetPolyOffsetRegister(cp, cmd_offset, value); }; g_hw_ctx_indirect_func[Pm4::PA_SU_POLY_OFFSET_FRONT_SCALE] = [](KYTY_HW_CTX_INDIRECT_ARGS) { - HwCtxIgnorePolyOffsetRegister(cmd_offset, value); + HwCtxSetPolyOffsetRegister(cp, cmd_offset, value); }; g_hw_ctx_indirect_func[Pm4::PA_SU_POLY_OFFSET_FRONT_OFFSET] = [](KYTY_HW_CTX_INDIRECT_ARGS) { - HwCtxIgnorePolyOffsetRegister(cmd_offset, value); + HwCtxSetPolyOffsetRegister(cp, cmd_offset, value); }; g_hw_ctx_indirect_func[Pm4::PA_SU_POLY_OFFSET_BACK_SCALE] = [](KYTY_HW_CTX_INDIRECT_ARGS) { - HwCtxIgnorePolyOffsetRegister(cmd_offset, value); + HwCtxSetPolyOffsetRegister(cp, cmd_offset, value); }; g_hw_ctx_indirect_func[Pm4::PA_SU_POLY_OFFSET_BACK_OFFSET] = [](KYTY_HW_CTX_INDIRECT_ARGS) { - HwCtxIgnorePolyOffsetRegister(cmd_offset, value); + HwCtxSetPolyOffsetRegister(cp, cmd_offset, value); }; g_hw_ctx_indirect_func[Pm4::DB_Z_INFO] = [](KYTY_HW_CTX_INDIRECT_ARGS) { diff --git a/src/graphics/guest_gpu/hardwareContext.h b/src/graphics/guest_gpu/hardwareContext.h index 4699e7dff..7f61648a8 100644 --- a/src/graphics/guest_gpu/hardwareContext.h +++ b/src/graphics/guest_gpu/hardwareContext.h @@ -348,6 +348,16 @@ struct ModeControl { bool persp_corr_dis = false; }; +struct PolyOffset { + int8_t neg_num_db_bits = -23; + bool db_is_float_fmt = true; + float clamp = 0.0f; + float front_scale = 0.0f; + float front_offset = 0.0f; + float back_scale = 0.0f; + float back_offset = 0.0f; +}; + struct BlendControl { uint8_t color_srcblend = 1; uint8_t color_comb_fcn = 0; @@ -905,6 +915,8 @@ public: void SetDepthControl(const DepthControl& control) { m_depth_control = control; } [[nodiscard]] const ModeControl& GetModeControl() const { return m_mode_control; } void SetModeControl(const ModeControl& control) { m_mode_control = control; } + [[nodiscard]] const PolyOffset& GetPolyOffset() const { return m_poly_offset; } + void SetPolyOffset(const PolyOffset& offset) { m_poly_offset = offset; } [[nodiscard]] const EqaaControl& GetEqaaControl() const { return m_eqaa_control; } void SetEqaaControl(const EqaaControl& control) { m_eqaa_control = control; } [[nodiscard]] const StencilControl& GetStencilControl() const { return m_stencil_control; } @@ -1002,6 +1014,7 @@ private: uint8_t m_stencil_clear_value = 0; ModeControl m_mode_control; + PolyOffset m_poly_offset; EqaaControl m_eqaa_control; ShaderRegisters m_sh_regs; diff --git a/src/graphics/guest_gpu/pm4.h b/src/graphics/guest_gpu/pm4.h index a5fb532a0..afe062896 100644 --- a/src/graphics/guest_gpu/pm4.h +++ b/src/graphics/guest_gpu/pm4.h @@ -578,24 +578,28 @@ constexpr uint32_t DB_HTILE_SURFACE_PREFETCH_HEIGHT_MASK = 0x3F; constexpr uint32_t DB_HTILE_SURFACE_DST_OUTSIDE_ZERO_TO_ONE_SHIFT = 16; constexpr uint32_t DB_HTILE_SURFACE_DST_OUTSIDE_ZERO_TO_ONE_MASK = 0x1; -constexpr uint32_t DB_SRESULTS_COMPARE_STATE0 = 0x2B0; -constexpr uint32_t DB_SRESULTS_COMPARE_STATE1 = 0x2B1; -constexpr uint32_t VGT_GS_MAX_VERT_OUT = 0x2CE; -constexpr uint32_t GE_NGG_SUBGRP_CNTL = 0x2D3; -constexpr uint32_t VGT_TESS_DISTRIBUTION = 0x2D4; -constexpr uint32_t VGT_SHADER_STAGES_EN = 0x2D5; -constexpr uint32_t VGT_LS_HS_CONFIG = 0x2D6; -constexpr uint32_t VGT_TF_PARAM = 0x2DB; -constexpr uint32_t DB_ALPHA_TO_MASK = 0x2DC; -constexpr uint32_t PA_SU_POLY_OFFSET_DB_FMT_CNTL = 0x2DE; -constexpr uint32_t PA_SU_POLY_OFFSET_CLAMP = 0x2DF; -constexpr uint32_t PA_SU_POLY_OFFSET_FRONT_SCALE = 0x2E0; -constexpr uint32_t PA_SU_POLY_OFFSET_FRONT_OFFSET = 0x2E1; -constexpr uint32_t PA_SU_POLY_OFFSET_BACK_SCALE = 0x2E2; -constexpr uint32_t PA_SU_POLY_OFFSET_BACK_OFFSET = 0x2E3; -constexpr uint32_t VGT_GS_INSTANCE_CNT = 0x2E4; -constexpr uint32_t PA_SC_CENTROID_PRIORITY_0 = 0x2F5; -constexpr uint32_t PA_SC_CENTROID_PRIORITY_1 = 0x2F6; +constexpr uint32_t DB_SRESULTS_COMPARE_STATE0 = 0x2B0; +constexpr uint32_t DB_SRESULTS_COMPARE_STATE1 = 0x2B1; +constexpr uint32_t VGT_GS_MAX_VERT_OUT = 0x2CE; +constexpr uint32_t GE_NGG_SUBGRP_CNTL = 0x2D3; +constexpr uint32_t VGT_TESS_DISTRIBUTION = 0x2D4; +constexpr uint32_t VGT_SHADER_STAGES_EN = 0x2D5; +constexpr uint32_t VGT_LS_HS_CONFIG = 0x2D6; +constexpr uint32_t VGT_TF_PARAM = 0x2DB; +constexpr uint32_t DB_ALPHA_TO_MASK = 0x2DC; +constexpr uint32_t PA_SU_POLY_OFFSET_DB_FMT_CNTL = 0x2DE; +constexpr uint32_t PA_SU_POLY_OFFSET_DB_FMT_CNTL_NEG_NUM_DB_BITS_SHIFT = 0; +constexpr uint32_t PA_SU_POLY_OFFSET_DB_FMT_CNTL_NEG_NUM_DB_BITS_MASK = 0xFF; +constexpr uint32_t PA_SU_POLY_OFFSET_DB_FMT_CNTL_DB_IS_FLOAT_FMT_SHIFT = 8; +constexpr uint32_t PA_SU_POLY_OFFSET_DB_FMT_CNTL_DB_IS_FLOAT_FMT_MASK = 0x1; +constexpr uint32_t PA_SU_POLY_OFFSET_CLAMP = 0x2DF; +constexpr uint32_t PA_SU_POLY_OFFSET_FRONT_SCALE = 0x2E0; +constexpr uint32_t PA_SU_POLY_OFFSET_FRONT_OFFSET = 0x2E1; +constexpr uint32_t PA_SU_POLY_OFFSET_BACK_SCALE = 0x2E2; +constexpr uint32_t PA_SU_POLY_OFFSET_BACK_OFFSET = 0x2E3; +constexpr uint32_t VGT_GS_INSTANCE_CNT = 0x2E4; +constexpr uint32_t PA_SC_CENTROID_PRIORITY_0 = 0x2F5; +constexpr uint32_t PA_SC_CENTROID_PRIORITY_1 = 0x2F6; constexpr uint32_t PA_SC_AA_CONFIG = 0x2F8; constexpr uint32_t PA_SC_AA_CONFIG_MSAA_NUM_SAMPLES_SHIFT = 0; diff --git a/src/graphics/host_gpu/renderer/debug.cpp b/src/graphics/host_gpu/renderer/debug.cpp index 7a8fe33b6..b3b466fe3 100644 --- a/src/graphics/host_gpu/renderer/debug.cpp +++ b/src/graphics/host_gpu/renderer/debug.cpp @@ -686,14 +686,6 @@ static void McCheck(const HW::ModeControl& c) { } EXIT_NOT_IMPLEMENTED(c.polymode_front_ptype != 0 && c.polymode_front_ptype != 2); EXIT_NOT_IMPLEMENTED(c.polymode_back_ptype != 0 && c.polymode_back_ptype != 2); - if (c.poly_offset_front_enable || c.poly_offset_back_enable) { - static bool logged = false; - if (!logged) { - LOGF("\t temporary: PA_SU_SC_MODE_CNTL.POLY_OFFSET_*_ENABLE is not implemented; " - "continuing without depth bias\n"); - logged = true; - } - } if (c.vtx_window_offset_enable) { static bool logged = false; if (!logged) { diff --git a/src/graphics/host_gpu/renderer/pipeline/shaders.cpp b/src/graphics/host_gpu/renderer/pipeline/shaders.cpp index d02731956..7a9243d97 100644 --- a/src/graphics/host_gpu/renderer/pipeline/shaders.cpp +++ b/src/graphics/host_gpu/renderer/pipeline/shaders.cpp @@ -947,6 +947,8 @@ void CreatePipelineInternal( vk::DynamicState::eViewport, vk::DynamicState::eScissor, vk::DynamicState::eLineWidth, + vk::DynamicState::eDepthBiasEnable, + vk::DynamicState::eDepthBias, vk::DynamicState::eStencilCompareMask, vk::DynamicState::eStencilReference, vk::DynamicState::eStencilWriteMask, diff --git a/src/graphics/host_gpu/renderer/renderDraw.cpp b/src/graphics/host_gpu/renderer/renderDraw.cpp index 70e13320f..fb775053f 100644 --- a/src/graphics/host_gpu/renderer/renderDraw.cpp +++ b/src/graphics/host_gpu/renderer/renderDraw.cpp @@ -64,12 +64,32 @@ int32_t ResolveVertexOffset(uint32_t index_offset, const ShaderVertexInputInfo& return 0; } -static std::atomic g_draw_state_log_count = 0; -static std::atomic g_draw_input_log_count = 0; -static std::atomic g_mrt_state_log_count = 0; -static std::atomic g_shader_stage_log_count = 0; +static std::atomic g_draw_state_log_count = 0; +static std::atomic g_draw_input_log_count = 0; +static std::atomic g_mrt_state_log_count = 0; +static std::atomic g_shader_stage_log_count = 0; + static std::atomic g_framebuffer_skip_log_count = 0; +static float ConvertPolygonOffsetConstantFactor(float guest_factor, const HW::PolyOffset& offset, + vk::Format host_depth_format) { + if (offset.db_is_float_fmt) { + return guest_factor; + } + + int host_depth_bits = 0; + switch (host_depth_format) { + case vk::Format::eD16Unorm: + case vk::Format::eD16UnormS8Uint: host_depth_bits = 16; break; + case vk::Format::eD24UnormS8Uint: host_depth_bits = 24; break; + default: + // A fixed-point guest bias cannot be represented exactly by a floating-point host + // attachment without VK_EXT_depth_bias_control. + return guest_factor; + } + return std::ldexp(guest_factor, host_depth_bits + offset.neg_num_db_bits); +} + static const char* RenderColorTypeName(RenderColorType type) { switch (type) { case RenderColorType::NoColorOutput: return "NoColorOutput"; @@ -335,6 +355,22 @@ static PipelineDynamicParameters BuildGraphicsDynamicParams(const RenderCommandB ret.stencil_front = depth.stencil_dynamic_front; ret.stencil_back = depth.stencil_dynamic_back; + const auto& mode = ctx.GetModeControl(); + const auto& poly_offset = ctx.GetPolyOffset(); + const bool use_front = mode.poly_offset_front_enable && !mode.cull_front; + const bool use_back = mode.poly_offset_back_enable && !mode.cull_back; + ret.depth_bias_enable = use_front || use_back; + if (ret.depth_bias_enable) { + // Vulkan has one bias for both faces. Prefer a visible front face when both are enabled. + const float guest_constant_factor = + use_front ? poly_offset.front_offset : poly_offset.back_offset; + ret.depth_bias_constant_factor = + ConvertPolygonOffsetConstantFactor(guest_constant_factor, poly_offset, depth.format); + ret.depth_bias_clamp = poly_offset.clamp; + ret.depth_bias_slope_factor = + (use_front ? poly_offset.front_scale : poly_offset.back_scale) / 16.0f; + } + // CB_COLOR_CONTROL.operation controls special CB operations, not the normal color component // write mask. Use CB_TARGET_MASK here so scanout passes with mode=Disable do not go black. for (uint32_t i = 0; i < RENDER_COLOR_ATTACHMENTS_MAX; i++) { @@ -379,6 +415,13 @@ static void SetDynamicParams(const RenderCommandBuffer& buffer, vk::CommandBuffe } vk_buffer.setLineWidth(line_width); + vk_buffer.setDepthBiasEnable(dynamic_params.depth_bias_enable ? VK_TRUE : VK_FALSE); + if (dynamic_params.depth_bias_enable) { + vk_buffer.setDepthBias(dynamic_params.depth_bias_constant_factor, + dynamic_params.depth_bias_clamp, + dynamic_params.depth_bias_slope_factor); + } + if (dynamic_params.stencil_test_enable) { vk_buffer.setStencilCompareMask(vk::StencilFaceFlagBits::eFront, dynamic_params.stencil_front.compareMask); diff --git a/src/graphics/host_gpu/renderer/renderDraw.h b/src/graphics/host_gpu/renderer/renderDraw.h index f17af2d49..40cbe54be 100644 --- a/src/graphics/host_gpu/renderer/renderDraw.h +++ b/src/graphics/host_gpu/renderer/renderDraw.h @@ -15,12 +15,16 @@ struct ShaderVertexInputInfo; struct PipelineDynamicParameters { bool stencil_test_enable = false; + bool depth_bias_enable = false; float viewport_scale[3] = {}; float viewport_offset[3] = {}; int scissor_ltrb[4] = {0}; float line_width = 1.0f; + float depth_bias_constant_factor = 0.0f; + float depth_bias_clamp = 0.0f; + float depth_bias_slope_factor = 0.0f; uint32_t color_write_count = 1; bool color_write_enable[RENDER_COLOR_ATTACHMENTS_MAX] = {true, true}; @@ -33,10 +37,6 @@ struct PipelineDynamicParameters { static_assert(std::is_trivially_copyable_v); static_assert(std::is_standard_layout_v); static_assert(alignof(PipelineDynamicParameters) == 1); -static_assert(sizeof(PipelineDynamicParameters) == - sizeof(bool) + sizeof(float[3]) + sizeof(float[3]) + sizeof(int[4]) + sizeof(float) + - sizeof(uint32_t) + sizeof(bool[RENDER_COLOR_ATTACHMENTS_MAX]) + - sizeof(PipelineStencilDynamicState) * 2); [[nodiscard]] int32_t ResolveVertexOffset(uint32_t index_offset, const ShaderVertexInputInfo& vs_input_info); diff --git a/src/graphics/presentation/window/vulkanWindow.cpp b/src/graphics/presentation/window/vulkanWindow.cpp index 73eb693eb..ceb807a9e 100644 --- a/src/graphics/presentation/window/vulkanWindow.cpp +++ b/src/graphics/presentation/window/vulkanWindow.cpp @@ -260,6 +260,10 @@ static void VulkanFindPhysicalDevice(vk::Instance instance, vk::SurfaceKHR surfa LOGF("sampleRateShading is not supported\n"); skip_device = true; } + if (device_features2.features.depthBiasClamp != VK_TRUE) { + LOGF("depthBiasClamp is not supported\n"); + skip_device = true; + } if (device_features2.features.fragmentStoresAndAtomics != VK_TRUE) { LOGF("fragmentStoresAndAtomics is not supported\n"); @@ -549,6 +553,7 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, const V EXIT_NOT_IMPLEMENTED(required_features13.synchronization2 == VK_TRUE && supported_features13.synchronization2 != VK_TRUE); EXIT_NOT_IMPLEMENTED(supported_features2.features.sampleRateShading != VK_TRUE); + EXIT_NOT_IMPLEMENTED(supported_features2.features.depthBiasClamp != VK_TRUE); features12.timelineSemaphore = VK_TRUE; vk::PhysicalDeviceFeatures device_features {}; @@ -564,6 +569,7 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, const V device_features.independentBlend = VK_TRUE; device_features.tessellationShader = VK_TRUE; device_features.sampleRateShading = VK_TRUE; + device_features.depthBiasClamp = VK_TRUE; graphics.sample_rate_shading_enabled = true; device_features.vertexPipelineStoresAndAtomics = supported_features2.features.vertexPipelineStoresAndAtomics; diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 1afd57040..ac9339ad8 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -18059,6 +18059,41 @@ void CheckPm4StencilInfoValueLane(RenderContext& renderer) { std::printf("[host] %-32s ok\n", "Pm4StencilInfoValueLane"); } +void CheckPm4PolygonOffsetRegisters(RenderContext& renderer) { + GraphicsInitJmpTables(); + CommandProcessor processor(renderer); + const auto& defaults = processor.GetCtx().GetPolyOffset(); + Require("Pm4PolygonOffset", "format defaults", + defaults.neg_num_db_bits == -23 && defaults.db_is_float_fmt, + "polygon offset Z format did not default to D32F"); + + constexpr std::array payload { + 0x000000f0u, std::bit_cast(2.5f), std::bit_cast(16.0f), + std::bit_cast(-3.25f), std::bit_cast(32.0f), + std::bit_cast(4.5f)}; + const auto consumed = HwCtxSetPolyOffsetRegisters( + processor, KYTY_PM4(8, Pm4::IT_SET_CONTEXT_REG, Pm4::R_ZERO), + Pm4::PA_SU_POLY_OFFSET_DB_FMT_CNTL, payload.data(), 0); + const auto& direct = processor.GetCtx().GetPolyOffset(); + Require("Pm4PolygonOffset", "direct registers", + consumed == payload.size() && direct.neg_num_db_bits == -16 && + !direct.db_is_float_fmt && direct.clamp == 2.5f && direct.front_scale == 16.0f && + direct.front_offset == -3.25f && direct.back_scale == 32.0f && + direct.back_offset == 4.5f, + "direct polygon offset register packet was decoded incorrectly"); + + g_hw_ctx_indirect_func[Pm4::PA_SU_POLY_OFFSET_DB_FMT_CNTL]( + processor, Pm4::PA_SU_POLY_OFFSET_DB_FMT_CNTL, 0x000001e9u); + g_hw_ctx_indirect_func[Pm4::PA_SU_POLY_OFFSET_FRONT_SCALE]( + processor, Pm4::PA_SU_POLY_OFFSET_FRONT_SCALE, std::bit_cast(24.0f)); + const auto& indirect = processor.GetCtx().GetPolyOffset(); + Require("Pm4PolygonOffset", "indirect registers", + indirect.neg_num_db_bits == -23 && indirect.db_is_float_fmt && + indirect.front_scale == 24.0f && indirect.front_offset == -3.25f, + "indirect polygon offset register write was decoded incorrectly"); + std::printf("[host] %-32s ok\n", "Pm4PolygonOffset"); +} + void CheckPm4ContextStateOperations(RenderContext& renderer) { GraphicsInitJmpTables(); CommandProcessor processor(renderer); @@ -18327,6 +18362,7 @@ int main(int argc, char** argv) { } if (argc == 2 && std::strcmp(argv[1], "--context-state-only") == 0) { VulkanHarness vulkan; + CheckPm4PolygonOffsetRegisters(vulkan.RuntimeRenderer()); CheckPm4ContextStateOperations(vulkan.RuntimeRenderer()); return 0; } @@ -18464,6 +18500,7 @@ int main(int argc, char** argv) { CheckVulkan13FeatureRequirements(); CheckPm4AcquireMemNoOp(vulkan.RuntimeRenderer()); CheckPm4StencilInfoValueLane(vulkan.RuntimeRenderer()); + CheckPm4PolygonOffsetRegisters(vulkan.RuntimeRenderer()); CheckPm4ContextStateOperations(vulkan.RuntimeRenderer()); CheckPm4WaitResume(vulkan.RuntimeRenderer()); CheckPm4CeCompletion(vulkan.RuntimeRenderer());