renderer: handle disabled clip space

Co-authored-by: IndecisiveTurtle <47210458+raphaelthegreat@users.noreply.github.com>
This commit is contained in:
Vladislav Mikhalin 2024-12-26 21:11:01 +03:00
parent 3b474a12f9
commit 50bbfc4f86
7 changed files with 72 additions and 21 deletions

View File

@ -24,10 +24,28 @@ void ConvertDepthMode(EmitContext& ctx) {
ctx.OpStore(ctx.output_position, vector);
}
void ConvertPositionToClipSpace(EmitContext& ctx) {
const Id type{ctx.F32[1]};
Id position{ctx.OpLoad(ctx.F32[4], ctx.output_position)};
const Id x{ctx.OpCompositeExtract(type, position, 0u)};
const Id y{ctx.OpCompositeExtract(type, position, 1u)};
const Id z{ctx.OpCompositeExtract(type, position, 2u)};
const Id w{ctx.OpCompositeExtract(type, position, 3u)};
const Id ndc_x{ctx.OpFSub(type, ctx.OpFDiv(type, x, ctx.Constant(type, float(8_KB))),
ctx.Constant(type, 1.f))};
const Id ndc_y{ctx.OpFSub(type, ctx.OpFDiv(type, y, ctx.Constant(type, float(8_KB))),
ctx.Constant(type, 1.f))};
const Id vector{ctx.OpCompositeConstruct(ctx.F32[4], std::array<Id, 4>({ndc_x, ndc_y, z, w}))};
ctx.OpStore(ctx.output_position, vector);
}
void EmitEpilogue(EmitContext& ctx) {
if (ctx.stage == Stage::Vertex && ctx.runtime_info.vs_info.emulate_depth_negative_one_to_one) {
ConvertDepthMode(ctx);
}
if (ctx.stage == Stage::Vertex && ctx.runtime_info.vs_info.clip_disable) {
ConvertPositionToClipSpace(ctx);
}
}
void EmitDiscard(EmitContext& ctx) {

View File

@ -84,6 +84,7 @@ struct VertexRuntimeInfo {
u32 num_outputs;
std::array<VsOutputMap, 3> outputs;
bool emulate_depth_negative_one_to_one{};
bool clip_disable{};
// Domain
AmdGpu::TessellationType tess_type;
AmdGpu::TessellationTopology tess_topology;
@ -92,7 +93,8 @@ struct VertexRuntimeInfo {
bool operator==(const VertexRuntimeInfo& other) const noexcept {
return emulate_depth_negative_one_to_one == other.emulate_depth_negative_one_to_one &&
tess_type == other.tess_type && tess_topology == other.tess_topology &&
clip_disable == other.clip_disable && tess_type == other.tess_type &&
tess_topology == other.tess_topology &&
tess_partitioning == other.tess_partitioning &&
hs_output_cp_stride == other.hs_output_cp_stride;
}

View File

@ -42,13 +42,14 @@ struct GraphicsPipelineKey {
vk::Format stencil_format;
struct {
bool clip_disable : 1;
bool depth_test_enable : 1;
bool depth_write_enable : 1;
bool depth_bounds_test_enable : 1;
bool depth_bias_enable : 1;
bool stencil_test_enable : 1;
// Must be named to be zero-initialized.
u8 _unused : 3;
u8 _unused : 2;
};
vk::CompareOp depth_compare_op;
@ -94,6 +95,10 @@ public:
return key.mrt_mask;
}
auto IsClipDisabled() const {
return key.clip_disable;
}
[[nodiscard]] bool IsPrimitiveListTopology() const {
return key.prim_type == AmdGpu::PrimitiveType::PointList ||
key.prim_type == AmdGpu::PrimitiveType::LineList ||

View File

@ -406,6 +406,7 @@ bool Instance::CreateDevice() {
},
vk::PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT{
.primitiveTopologyListRestart = true,
.primitiveTopologyPatchListRestart = true,
},
vk::PhysicalDeviceFragmentShaderBarycentricFeaturesKHR{
.fragmentShaderBarycentric = true,

View File

@ -125,6 +125,7 @@ const Shader::RuntimeInfo& PipelineCache::BuildRuntimeInfo(Stage stage, LogicalS
info.vs_info.emulate_depth_negative_one_to_one =
!instance.IsDepthClipControlSupported() &&
regs.clipper_control.clip_space == Liverpool::ClipSpace::MinusWToW;
info.vs_info.clip_disable = graphics_key.clip_disable;
if (l_stage == LogicalStage::TessellationEval) {
info.vs_info.tess_type = regs.tess_config.type;
info.vs_info.tess_topology = regs.tess_config.topology;
@ -261,6 +262,15 @@ bool PipelineCache::RefreshGraphicsKey() {
auto& regs = liverpool->regs;
auto& key = graphics_key;
const auto& vp_ctl = regs.viewport_control;
// TODO(roamic): the statement below needs verification with a sample on the real HW.
// If there is no defined transform to convert from clip space to screen space we assume that
// clipping is also disabled.
const bool viewport_disabled = !vp_ctl.xoffset_enable && !vp_ctl.xscale_enable &&
!vp_ctl.yoffset_enable && !vp_ctl.yscale_enable &&
!vp_ctl.zoffset_enable && !vp_ctl.zscale_enable;
key.clip_disable = regs.clipper_control.clip_disable || viewport_disabled;
key.depth_test_enable = regs.depth_control.depth_enable;
key.depth_write_enable =
regs.depth_control.depth_write_enable && !regs.depth_render_control.depth_clear_enable;

View File

@ -1032,7 +1032,7 @@ void Rasterizer::UnmapMemory(VAddr addr, u64 size) {
}
void Rasterizer::UpdateDynamicState(const GraphicsPipeline& pipeline) {
UpdateViewportScissorState();
UpdateViewportScissorState(pipeline);
auto& regs = liverpool->regs;
const auto cmdbuf = scheduler.CommandBuffer();
@ -1112,7 +1112,7 @@ void Rasterizer::UpdateDynamicState(const GraphicsPipeline& pipeline) {
}
}
void Rasterizer::UpdateViewportScissorState() {
void Rasterizer::UpdateViewportScissorState(const GraphicsPipeline& pipeline) {
const auto& regs = liverpool->regs;
const auto combined_scissor_value_tl = [](s16 scr, s16 win, s16 gen, s16 win_offset) {
@ -1157,20 +1157,6 @@ void Rasterizer::UpdateViewportScissorState() {
if (vp.xscale == 0) {
continue;
}
const auto xoffset = vp_ctl.xoffset_enable ? vp.xoffset : 0.f;
const auto xscale = vp_ctl.xscale_enable ? vp.xscale : 1.f;
const auto yoffset = vp_ctl.yoffset_enable ? vp.yoffset : 0.f;
const auto yscale = vp_ctl.yscale_enable ? vp.yscale : 1.f;
const auto zoffset = vp_ctl.zoffset_enable ? vp.zoffset : 0.f;
const auto zscale = vp_ctl.zscale_enable ? vp.zscale : 1.f;
viewports.push_back({
.x = xoffset - xscale,
.y = yoffset - yscale,
.width = xscale * 2.0f,
.height = yscale * 2.0f,
.minDepth = zoffset - zscale * reduce_z,
.maxDepth = zscale + zoffset,
});
auto vp_scsr = scsr;
if (regs.mode_control.vport_scissor_enable) {
@ -1187,13 +1173,42 @@ void Rasterizer::UpdateViewportScissorState() {
.offset = {vp_scsr.top_left_x, vp_scsr.top_left_y},
.extent = {vp_scsr.GetWidth(), vp_scsr.GetHeight()},
});
if (pipeline.IsClipDisabled()) {
// In case if clipping is disabled we patch the shader to convert vertex position
// from screen space coordinates to NDC by defining a render space as full hardware
// window range [0..16383, 0..16383] and setting the viewport to its size.
viewports.push_back({
.x = enable_offset ? float(regs.window_offset.window_x_offset) : 0.f,
.y = enable_offset ? float(regs.window_offset.window_y_offset) : 0.f,
.width = float(16_KB),
.height = float(16_KB),
.minDepth = 0.0,
.maxDepth = 1.0,
});
} else {
const auto xoffset = vp_ctl.xoffset_enable ? vp.xoffset : 0.f;
const auto xscale = vp_ctl.xscale_enable ? vp.xscale : 1.f;
const auto yoffset = vp_ctl.yoffset_enable ? vp.yoffset : 0.f;
const auto yscale = vp_ctl.yscale_enable ? vp.yscale : 1.f;
const auto zoffset = vp_ctl.zoffset_enable ? vp.zoffset : 0.f;
const auto zscale = vp_ctl.zscale_enable ? vp.zscale : 1.f;
viewports.push_back({
.x = xoffset - xscale,
.y = yoffset - yscale,
.width = xscale * 2.0f,
.height = yscale * 2.0f,
.minDepth = zoffset - zscale * reduce_z,
.maxDepth = zscale + zoffset,
});
}
}
if (viewports.empty()) {
// Vulkan requires providing at least one viewport.
constexpr vk::Viewport empty_viewport = {
.x = 0.0f,
.y = 0.0f,
.x = -1.0f,
.y = -1.0f,
.width = 1.0f,
.height = 1.0f,
.minDepth = 0.0f,

View File

@ -76,7 +76,7 @@ private:
void EliminateFastClear();
void UpdateDynamicState(const GraphicsPipeline& pipeline);
void UpdateViewportScissorState();
void UpdateViewportScissorState(const GraphicsPipeline& pipeline);
bool FilterDraw();