mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-19 18:36:53 +02:00
renderer: emulate clip-disabled screen-space positions
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ struct PipelineCache::ProgramCache {
|
||||
}
|
||||
|
||||
static constexpr std::size_t MaxStaticKeyWords =
|
||||
5 + ShaderVertexInputInfo::RES_MAX * 17;
|
||||
12 + ShaderVertexInputInfo::RES_MAX * 17;
|
||||
|
||||
template <typename InputInfo>
|
||||
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<float>(std::min(limits.maxViewportDimensions[0], 16384u)) * 0.5f;
|
||||
clip.half_extent[1] =
|
||||
static_cast<float>(std::min(limits.maxViewportDimensions[1], 16384u)) * 0.5f;
|
||||
clip.enabled = true;
|
||||
}
|
||||
Common::LockGuard lock(m_mutex);
|
||||
return m_program_cache->Get(params, input_info);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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<float>(std::min(limits.maxViewportDimensions[0], 16384u));
|
||||
viewport.height = static_cast<float>(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;
|
||||
|
||||
@@ -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)),
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <bit>
|
||||
#include <cstdio>
|
||||
#include <filesystem>
|
||||
#include <fmt/format.h>
|
||||
@@ -897,6 +898,18 @@ void BuildStageStaticKey(const ShaderVertexInputInfo& info, std::vector<uint32_t
|
||||
key.push_back(info.resources_num);
|
||||
key.push_back(info.scratch_size_dwords);
|
||||
key.push_back(info.pa_cl_vs_out_cntl);
|
||||
key.push_back(static_cast<uint32_t>(info.clip_space.enabled));
|
||||
if (info.clip_space.enabled) {
|
||||
for (const float value: info.clip_space.scale) {
|
||||
key.push_back(std::bit_cast<uint32_t>(value));
|
||||
}
|
||||
for (const float value: info.clip_space.offset) {
|
||||
key.push_back(std::bit_cast<uint32_t>(value));
|
||||
}
|
||||
for (const float value: info.clip_space.half_extent) {
|
||||
key.push_back(std::bit_cast<uint32_t>(value));
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < info.resources_num; i++) {
|
||||
const auto& resource = info.resources[i];
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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<const uint32_t> shader, uint32_t control) {
|
||||
ShaderVertexInputInfo vertex{};
|
||||
@@ -11908,6 +11947,7 @@ int main() {
|
||||
TestNewShaderRecompilerSpirvSizeBaselines();
|
||||
TestDemandDrivenSpirvDeclarations();
|
||||
TestNewShaderRecompilerSMovB32();
|
||||
TestNewShaderRecompilerClipDisabledPosition();
|
||||
TestNewShaderRecompilerAuxPositionExports();
|
||||
TestNewShaderRecompilerNativeWideScalarMemoryIr();
|
||||
TestNewShaderRecompilerNativeWideBufferIr();
|
||||
|
||||
Reference in New Issue
Block a user