renderer: handle disabled clipping (#2146)

Co-authored-by: IndecisiveTurtle <47210458+raphaelthegreat@users.noreply.github.com>
This commit is contained in:
Vladislav Mikhalin 2025-01-18 09:20:38 +03:00 committed by GitHub
parent a5440e0e43
commit 7b8177f48e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 149 additions and 35 deletions

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

@ -208,6 +208,7 @@ std::string Instance::GetDriverVersionName() {
bool Instance::CreateDevice() {
const vk::StructureChain feature_chain = physical_device.getFeatures2<
vk::PhysicalDeviceFeatures2, vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT,
vk::PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT,
vk::PhysicalDeviceExtendedDynamicState2FeaturesEXT,
vk::PhysicalDeviceExtendedDynamicState3FeaturesEXT,
vk::PhysicalDeviceCustomBorderColorFeaturesEXT,
@ -317,6 +318,9 @@ bool Instance::CreateDevice() {
.pQueuePriorities = queue_priorities.data(),
};
const auto topology_list_restart_features =
feature_chain.get<vk::PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT>();
const auto vk12_features = feature_chain.get<vk::PhysicalDeviceVulkan12Features>();
vk::StructureChain device_chain = {
vk::DeviceCreateInfo{
@ -406,6 +410,8 @@ bool Instance::CreateDevice() {
},
vk::PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT{
.primitiveTopologyListRestart = true,
.primitiveTopologyPatchListRestart =
topology_list_restart_features.primitiveTopologyPatchListRestart,
},
vk::PhysicalDeviceFragmentShaderBarycentricFeaturesKHR{
.fragmentShaderBarycentric = true,

View file

@ -279,6 +279,14 @@ public:
return min_imported_host_pointer_alignment;
}
u32 GetMaxViewportWidth() const {
return properties.limits.maxViewportDimensions[0];
}
u32 GetMaxViewportHeight() const {
return properties.limits.maxViewportDimensions[1];
}
/// Returns the sample count flags supported by framebuffers.
vk::SampleCountFlags GetFramebufferSampleCounts() const {
return properties.limits.framebufferColorSampleCounts &

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;
@ -210,6 +211,8 @@ PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_,
instance.GetDriverID() == vk::DriverId::eNvidiaProprietary,
.needs_lds_barriers = instance.GetDriverID() == vk::DriverId::eNvidiaProprietary ||
instance.GetDriverID() == vk::DriverId::eMoltenvk,
.max_viewport_width = instance.GetMaxViewportWidth(),
.max_viewport_height = instance.GetMaxViewportHeight(),
};
auto [cache_result, cache] = instance.GetDevice().createPipelineCacheUnique({});
ASSERT_MSG(cache_result == vk::Result::eSuccess, "Failed to create pipeline cache: {}",
@ -262,6 +265,8 @@ bool PipelineCache::RefreshGraphicsKey() {
auto& regs = liverpool->regs;
auto& key = graphics_key;
key.clip_disable =
regs.clipper_control.clip_disable || regs.primitive_type == AmdGpu::PrimitiveType::RectList;
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

@ -504,6 +504,17 @@ bool Rasterizer::BindResources(const Pipeline* pipeline) {
}
push_data.step0 = regs.vgt_instance_step_rate_0;
push_data.step1 = regs.vgt_instance_step_rate_1;
// TODO(roamic): add support for multiple viewports and geometry shaders when ViewportIndex
// is encountered and implemented in the recompiler.
if (stage->stage == Shader::Stage::Vertex) {
push_data.xoffset =
regs.viewport_control.xoffset_enable ? regs.viewports[0].xoffset : 0.f;
push_data.xscale = regs.viewport_control.xscale_enable ? regs.viewports[0].xscale : 1.f;
push_data.yoffset =
regs.viewport_control.yoffset_enable ? regs.viewports[0].yoffset : 0.f;
push_data.yscale = regs.viewport_control.yscale_enable ? regs.viewports[0].yscale : 1.f;
}
stage->PushUd(binding, push_data);
BindBuffers(*stage, binding, push_data, set_writes, buffer_barriers);
@ -1032,7 +1043,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 +1123,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) {
@ -1151,26 +1162,46 @@ void Rasterizer::UpdateViewportScissorState() {
? 1.0f
: 0.0f;
if (regs.polygon_control.enable_window_offset) {
LOG_ERROR(Render_Vulkan,
"PA_SU_SC_MODE_CNTL.VTX_WINDOW_OFFSET_ENABLE support is not yet implemented.");
}
for (u32 i = 0; i < Liverpool::NumViewports; i++) {
const auto& vp = regs.viewports[i];
const auto& vp_d = regs.viewport_depths[i];
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,
});
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 = 0.f,
.y = 0.f,
.width = float(std::min<u32>(instance.GetMaxViewportWidth(), 16_KB)),
.height = float(std::min<u32>(instance.GetMaxViewportHeight(), 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,
});
}
auto vp_scsr = scsr;
if (regs.mode_control.vport_scissor_enable) {
@ -1192,8 +1223,8 @@ void Rasterizer::UpdateViewportScissorState() {
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();