diff --git a/src/graphics/host_gpu/renderer/debug.cpp b/src/graphics/host_gpu/renderer/debug.cpp index 98b5fad7e..2f897ef8e 100644 --- a/src/graphics/host_gpu/renderer/debug.cpp +++ b/src/graphics/host_gpu/renderer/debug.cpp @@ -490,7 +490,7 @@ static void ClipCheck(const HW::ClipControl& c) { // dx_linear_attr_clip_enable preserves linear (noperspective) attributes at clip-generated // vertices, which Vulkan provides as part of clipping and interpolation. EXIT_NOT_IMPLEMENTED(c.user_clip_planes != 0 || c.user_clip_plane_mode != 0 || - c.vertex_kill_any || c.user_clip_plane_negate_y || c.clip_disable || + c.vertex_kill_any || c.user_clip_plane_negate_y || c.user_clip_plane_cull_only || c.cull_on_clipping_error_disable || c.force_viewport_index_from_vs_enable); } diff --git a/src/graphics/host_gpu/renderer/pipeline/pipelineCache.cpp b/src/graphics/host_gpu/renderer/pipeline/pipelineCache.cpp index 60461c05f..90947b97c 100644 --- a/src/graphics/host_gpu/renderer/pipeline/pipelineCache.cpp +++ b/src/graphics/host_gpu/renderer/pipeline/pipelineCache.cpp @@ -116,7 +116,7 @@ struct PipelineCache::ProgramCache { } static constexpr std::size_t MaxStaticKeyWords = - 5 + ShaderVertexInputInfo::RES_MAX * 17; + 12 + ShaderVertexInputInfo::RES_MAX * 17; template ShaderProgram Get(const ShaderParams& params, InputInfo& input_info) { @@ -340,8 +340,23 @@ void PipelineCache::Save() { ShaderProgram PipelineCache::GetVertexProgram(const HW::VertexShaderInfo& regs, const HW::ShaderRegisters& sh, + const HW::Context& context, ShaderVertexInputInfo& input_info) { const auto params = PrepareProgram(regs, sh, input_info); + if (context.GetClipControl().clip_disable) { + const auto& viewport = context.GetScreenViewport().viewports[0]; + const auto& limits = m_graphics.GetPhysicalDeviceProperties().limits; + auto& clip = input_info.clip_space; + clip.scale[0] = viewport.xscale; + clip.scale[1] = viewport.yscale; + clip.offset[0] = viewport.xoffset; + clip.offset[1] = viewport.yoffset; + clip.half_extent[0] = + static_cast(std::min(limits.maxViewportDimensions[0], 16384u)) * 0.5f; + clip.half_extent[1] = + static_cast(std::min(limits.maxViewportDimensions[1], 16384u)) * 0.5f; + clip.enabled = true; + } Common::LockGuard lock(m_mutex); return m_program_cache->Get(params, input_info); } diff --git a/src/graphics/host_gpu/renderer/pipeline/pipelineCache.h b/src/graphics/host_gpu/renderer/pipeline/pipelineCache.h index e6574f2cd..911fb33a9 100644 --- a/src/graphics/host_gpu/renderer/pipeline/pipelineCache.h +++ b/src/graphics/host_gpu/renderer/pipeline/pipelineCache.h @@ -130,6 +130,7 @@ public: ShaderProgram GetVertexProgram(const HW::VertexShaderInfo& regs, const HW::ShaderRegisters& sh, + const HW::Context& context, ShaderVertexInputInfo& input_info); ShaderProgram GetPixelProgram(const HW::PixelShaderInfo& regs, const HW::ShaderRegisters& sh, diff --git a/src/graphics/host_gpu/renderer/renderDraw.cpp b/src/graphics/host_gpu/renderer/renderDraw.cpp index feb966a6a..bc040b8d8 100644 --- a/src/graphics/host_gpu/renderer/renderDraw.cpp +++ b/src/graphics/host_gpu/renderer/renderDraw.cpp @@ -331,10 +331,18 @@ static void SetGraphicsDynamicParams(const CommandBuffer& buffer, vk::CommandBuf const auto final_scissor = calc_final_scissor(vp, ctx.GetScanModeControl(), framebuffer_extent); vk::Viewport viewport {}; - viewport.x = vp.viewports[0].xoffset - vp.viewports[0].xscale; - viewport.y = vp.viewports[0].yoffset - vp.viewports[0].yscale; - viewport.width = vp.viewports[0].xscale * 2.0f; - viewport.height = vp.viewports[0].yscale * 2.0f; + if (ctx.GetClipControl().clip_disable) { + const auto& limits = buffer.GetGraphics().GetPhysicalDeviceProperties().limits; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = static_cast(std::min(limits.maxViewportDimensions[0], 16384u)); + viewport.height = static_cast(std::min(limits.maxViewportDimensions[1], 16384u)); + } else { + viewport.x = vp.viewports[0].xoffset - vp.viewports[0].xscale; + viewport.y = vp.viewports[0].yoffset - vp.viewports[0].yscale; + viewport.width = vp.viewports[0].xscale * 2.0f; + viewport.height = vp.viewports[0].yscale * 2.0f; + } viewport.minDepth = vp.viewports[0].zoffset; viewport.maxDepth = vp.viewports[0].zscale + vp.viewports[0].zoffset; vk_buffer.setViewport(0, 1, &viewport); @@ -1013,8 +1021,8 @@ static void RefreshShaders(CommandBuffer& buffer, const DrawCallInfo& draw, bool LogDrawPhase(draw.name, "GetVertexProgram"); } auto& pipeline_cache = buffer.GetContext().GetPipelineCache(); - state.vertex_program = - pipeline_cache.GetVertexProgram(vertex_shader_info, shader_regs, state.vs_input_info); + state.vertex_program = pipeline_cache.GetVertexProgram(vertex_shader_info, shader_regs, ctx, + state.vs_input_info); if (!state.ps_active) { return; diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterFlow.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterFlow.cpp index a2433a766..2f54ea6ad 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterFlow.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterFlow.cpp @@ -408,6 +408,41 @@ void EmitAuxPositionExport(ValueEmitContext& ctx, uint32_t data, const IR::Expor } } +uint32_t ConvertClipCoordinate(EmitterState& state, uint32_t coordinate, float scale, + float offset, float half_extent) { + const auto window = state.builder.AllocateId(); + const auto biased = state.builder.AllocateId(); + const auto divided = state.builder.AllocateId(); + const auto ndc = state.builder.AllocateId(); + state.builder.AddFunction( + {OpFMul, TypeF32(state), window, coordinate, ConstantF32Value(state, scale)}); + state.builder.AddFunction( + {OpFAdd, TypeF32(state), biased, window, ConstantF32Value(state, offset)}); + state.builder.AddFunction( + {OpFDiv, TypeF32(state), divided, biased, ConstantF32Value(state, half_extent)}); + state.builder.AddFunction( + {OpFSub, TypeF32(state), ndc, divided, ConstantF32Value(state, 1.0f)}); + return ndc; +} + +uint32_t ConvertPositionToClipSpace(EmitterState& state, uint32_t position) { + const auto& transform = state.input_info.vertex->clip_space; + uint32_t components[4] {}; + for (uint32_t i = 0; i < 4; i++) { + components[i] = state.builder.AllocateId(); + state.builder.AddFunction( + {OpCompositeExtract, TypeF32(state), components[i], position, i}); + } + components[0] = ConvertClipCoordinate(state, components[0], transform.scale[0], + transform.offset[0], transform.half_extent[0]); + components[1] = ConvertClipCoordinate(state, components[1], transform.scale[1], + transform.offset[1], transform.half_extent[1]); + const auto converted = state.builder.AllocateId(); + state.builder.AddFunction({OpCompositeConstruct, TypeF32Vector(state, 4), converted, + components[0], components[1], components[2], components[3]}); + return converted; +} + void EmitExport(ValueEmitContext& ctx, const IR::Inst& inst) { auto& state = ctx.state; const auto& exp = ctx.Export(inst); @@ -467,6 +502,9 @@ void EmitExport(ValueEmitContext& ctx, const IR::Inst& inst) { } } if (exp.kind == IR::ExportTargetKind::Position) { + if (state.stage == ShaderType::Vertex && state.input_info.vertex->clip_space.enabled) { + value = ConvertPositionToClipSpace(state, value); + } const auto pointer = state.builder.AllocateId(); state.builder.AddFunction( {OpAccessChain, TypePointer(state, StorageClassOutput, TypeF32Vector(state, 4)), diff --git a/src/graphics/shader/shader.cpp b/src/graphics/shader/shader.cpp index 106e3f147..8bb5a1bfa 100644 --- a/src/graphics/shader/shader.cpp +++ b/src/graphics/shader/shader.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -897,6 +898,18 @@ void BuildStageStaticKey(const ShaderVertexInputInfo& info, std::vector(info.clip_space.enabled)); + if (info.clip_space.enabled) { + for (const float value: info.clip_space.scale) { + key.push_back(std::bit_cast(value)); + } + for (const float value: info.clip_space.offset) { + key.push_back(std::bit_cast(value)); + } + for (const float value: info.clip_space.half_extent) { + key.push_back(std::bit_cast(value)); + } + } for (int i = 0; i < info.resources_num; i++) { const auto& resource = info.resources[i]; diff --git a/src/graphics/shader/shader.h b/src/graphics/shader/shader.h index a0c4699e9..7790724ef 100644 --- a/src/graphics/shader/shader.h +++ b/src/graphics/shader/shader.h @@ -68,6 +68,13 @@ inline uint8_t GetDstSel(uint32_t swizzle, uint32_t channel) { return (swizzle >> (channel * 3u)) & 0x7u; } +struct ShaderClipSpaceTransform { + float scale[2] = {}; + float offset[2] = {}; + float half_extent[2] = {}; + bool enabled = false; +}; + struct ShaderVertexInputInfo { static constexpr int RES_MAX = 32; @@ -84,6 +91,7 @@ struct ShaderVertexInputInfo { uint32_t scratch_size_dwords = 0; uint32_t param_export_mask = 0; uint32_t pa_cl_vs_out_cntl = 0; + ShaderClipSpaceTransform clip_space; bool fetch_external = false; bool fetch_embedded = false; }; diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index be73198fc..c2a67b009 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -8811,6 +8811,45 @@ void TestNewShaderRecompilerExpVertexOutputs() { CheckSpirvBinaryValidates(result.spirv); } +void TestNewShaderRecompilerClipDisabledPosition() { + const uint32_t shader[] = { + EncodeExp0(0x0c, 0xf), EncodeExp1(0, 1, 2, 3), 0xbf810000u, + }; + const auto compile = [&](const ShaderVertexInputInfo &vertex) { + auto options = MakeCompileOptions(ShaderType::Vertex); + options.input_info.vertex = &vertex; + return ShaderRecompiler::Recompile(shader, options); + }; + + ShaderVertexInputInfo regular{}; + auto clipped = regular; + clipped.clip_space = { + .scale = {640.0f, 360.0f}, + .offset = {640.0f, 360.0f}, + .half_extent = {8192.0f, 8192.0f}, + .enabled = true, + }; + const auto regular_result = compile(regular); + const auto clipped_result = compile(clipped); + CheckSpirvBinaryValidates(clipped_result.spirv); + Check(SpirvInstructionOpcodeCount(regular_result.spirv, 136u) == 0u && + SpirvInstructionOpcodeCount(clipped_result.spirv, 133u) == 2u && + SpirvInstructionOpcodeCount(clipped_result.spirv, 129u) == 2u && + SpirvInstructionOpcodeCount(clipped_result.spirv, 136u) == 2u && + SpirvInstructionOpcodeCount(clipped_result.spirv, 131u) == 2u, + "clip-disabled position export did not convert both screen coordinates to NDC"); + Check(MakeStageStaticKey(regular) != MakeStageStaticKey(clipped), + "clip-disabled vertex shader shared the regular shader cache key"); + auto disabled = clipped; + disabled.clip_space.enabled = false; + Check(MakeStageStaticKey(regular) == MakeStageStaticKey(disabled), + "disabled clip-space payload affected the shader cache key"); + auto shifted = clipped; + shifted.clip_space.offset[0] += 1.0f; + Check(MakeStageStaticKey(clipped) != MakeStageStaticKey(shifted), + "clip-disabled viewport transform is absent from the shader cache key"); +} + void TestNewShaderRecompilerAuxPositionExports() { const auto compile = [](std::span shader, uint32_t control) { ShaderVertexInputInfo vertex{}; @@ -11908,6 +11947,7 @@ int main() { TestNewShaderRecompilerSpirvSizeBaselines(); TestDemandDrivenSpirvDeclarations(); TestNewShaderRecompilerSMovB32(); + TestNewShaderRecompilerClipDisabledPosition(); TestNewShaderRecompilerAuxPositionExports(); TestNewShaderRecompilerNativeWideScalarMemoryIr(); TestNewShaderRecompilerNativeWideBufferIr();