[Vulkan] Add depth_float24_convert_in_pixel_shader support

Co-authored-by: Herman S. <429230+has207@users.noreply.github.com>
This commit is contained in:
bomabomabomaboma
2026-08-01 00:00:51 +00:00
committed by Radosław Gliński
parent 947075f880
commit 2a6e9f4e2a
9 changed files with 301 additions and 71 deletions

View File

@@ -428,6 +428,8 @@ namespace spv {
#define ExecutionModeVertexOrderCw ExecutionMode::VertexOrderCw
#define ExecutionModeVertexOrderCcw ExecutionMode::VertexOrderCcw
#define ExecutionModeDepthReplacing ExecutionMode::DepthReplacing
#define ExecutionModeDepthGreater ExecutionMode::DepthGreater
#define ExecutionModeDepthLess ExecutionMode::DepthLess
#define ExecutionModeStencilRefReplacingEXT \
ExecutionMode::StencilRefReplacingEXT
#define ExecutionModeLocalSize ExecutionMode::LocalSize
@@ -438,6 +440,7 @@ namespace spv {
#define DecorationBufferBlock Decoration::BufferBlock
#define DecorationCoherent Decoration::Coherent
#define DecorationCentroid Decoration::Centroid
#define DecorationSample Decoration::Sample
#define DecorationSpecId Decoration::SpecId
#define DecorationNonReadable Decoration::NonReadable

View File

@@ -92,14 +92,18 @@ uint64_t SpirvShaderTranslator::GetDefaultPixelShaderModification(
return shader_modification.value;
}
std::vector<uint8_t> SpirvShaderTranslator::CreateDepthOnlyFragmentShader() {
std::vector<uint8_t> SpirvShaderTranslator::CreateDepthOnlyFragmentShader(
Modification::DepthStencilMode depth_stencil_mode) {
is_depth_only_fragment_shader_ = true;
// TODO(Triang3l): Handle in a nicer way (is_depth_only_fragment_shader_ is a
// leftover from when a Shader object wasn't used during translation).
Shader shader(xenos::ShaderType::kPixel, 0, nullptr, 0);
StringBuffer instruction_disassembly_buffer;
shader.AnalyzeUcode(instruction_disassembly_buffer);
Shader::Translation& translation = *shader.GetOrCreateTranslation(0);
Modification modification(0);
modification.pixel.depth_stencil_mode = depth_stencil_mode;
Shader::Translation& translation =
*shader.GetOrCreateTranslation(modification.value);
TranslateAnalyzedShader(translation);
is_depth_only_fragment_shader_ = false;
return translation.translated_binary();
@@ -734,9 +738,19 @@ std::vector<uint8_t> SpirvShaderTranslator::CompleteTranslation() {
builder_->addExecutionMode(function_main_,
spv::ExecutionModeEarlyFragmentTests);
}
if (current_shader().writes_depth() && !edram_fragment_shader_interlock_) {
// FSI handles depth manually.
if (!edram_fragment_shader_interlock_ &&
(current_shader().writes_depth() || DSV_IsWritingFloat24Depth())) {
builder_->addExecutionMode(function_main_,
spv::ExecutionModeDepthReplacing);
// Truncating float24 conversion of the rasterizer's own depth rounds
// towards zero, so the output is always <= the original - announce that
// to keep coarse early-Z culling possible.
if (!current_shader().writes_depth() &&
GetSpirvShaderModification().pixel.depth_stencil_mode ==
Modification::DepthStencilMode::kFloat24Truncating) {
builder_->addExecutionMode(function_main_, spv::ExecutionModeDepthLess);
}
}
if (edram_fragment_shader_interlock_) {
// Accessing per-sample values, so interlocking just when there's common
@@ -2246,14 +2260,15 @@ void SpirvShaderTranslator::StartFragmentShaderBeforeMain() {
}
// Fragment coordinates.
// TODO(Triang3l): More conditions - depth writing in the fragment shader
// (per-sample if supported).
// FSI: Always needed for EDRAM offset calculation and depth derivatives.
// param_gen: Needed for PsParamGen calculation.
// FBO alpha-to-coverage: Needed for dithering pattern, but only when
// alpha-to-coverage can actually run (no early fragment tests).
// FBO float24 in-PS conversion of the rasterizer's depth: reads
// gl_FragCoord.z
// - and must do so per-sample for MSAA antialiasing of intersections.
bool need_frag_coord =
edram_fragment_shader_interlock_ || param_gen_needed ||
edram_fragment_shader_interlock_ || param_gen_needed || IsSampleRate() ||
(!edram_fragment_shader_interlock_ && !is_depth_only_fragment_shader_ &&
current_shader().writes_color_target(0) &&
!IsExecutionModeEarlyFragmentTests());
@@ -2262,6 +2277,13 @@ void SpirvShaderTranslator::StartFragmentShaderBeforeMain() {
spv::NoPrecision, spv::StorageClassInput, type_float4_, "gl_FragCoord");
builder_->addDecoration(input_fragment_coordinates_, spv::DecorationBuiltIn,
static_cast<int>(spv::BuiltIn::FragCoord));
if (IsSampleRate()) {
// Per the Vulkan spec, a Sample-decorated fragment input forces
// per-sample shader invocation - no explicit sampleShadingEnable needed.
builder_->addCapability(spv::CapabilitySampleRateShading);
builder_->addDecoration(input_fragment_coordinates_,
spv::DecorationSample);
}
main_interface_.push_back(input_fragment_coordinates_);
}
@@ -2328,10 +2350,12 @@ void SpirvShaderTranslator::StartFragmentShaderBeforeMain() {
}
// Fragment depth output (gl_FragDepth) for the FBO path.
// Created when the guest pixel shader writes oDepth.
// Created when the guest pixel shader writes oDepth, or when the host depth
// buffer is float24 and the rasterizer's own depth needs in-PS conversion
// (including the synthetic depth-only shader used for no-PS guest draws).
// FSI manages its own depth and does not need an Output.
if (!edram_fragment_shader_interlock_ && !is_depth_only_fragment_shader_ &&
current_shader().writes_depth()) {
if (!edram_fragment_shader_interlock_ &&
(current_shader().writes_depth() || DSV_IsWritingFloat24Depth())) {
output_fragment_depth_ = builder_->createVariable(
spv::NoPrecision, spv::StorageClassOutput, type_float_, "gl_FragDepth");
builder_->addDecoration(output_fragment_depth_, spv::DecorationBuiltIn,

View File

@@ -34,15 +34,24 @@ class SpirvShaderTranslator : public ShaderTranslator {
// TODO(Triang3l): Change to 0xYYYYMMDD once it's out of the rapid
// prototyping stage (easier to do small granular updates with an
// incremental counter).
static constexpr uint32_t kVersion = 12;
static constexpr uint32_t kVersion = 13;
enum class DepthStencilMode : uint32_t {
kNoModifiers,
// Early fragment tests - enable if alpha test and alpha to coverage are
// disabled; ignored if anything in the shader blocks early Z writing.
kEarlyHint,
// TODO(Triang3l): Unorm24 (rounding) and float24 (truncating and
// rounding) output modes.
// Converting the depth to the closest 32-bit float representable exactly
// as a 20e4 float, truncating towards zero, so SV_DepthLessEqual-style
// conservative depth output (ExecutionModeDepthLess) can still allow
// coarse early Z culling. MSAA depth must be per-sample, so the shader
// runs at sample frequency.
// Fixed-function viewport depth bounds must be snapped to float24 too.
kFloat24Truncating,
// Similar to kFloat24Truncating, but rounding to the nearest even, so
// plain ExecutionModeDepthReplacing is used rather than DepthLess.
kFloat24Rounding,
// TODO(Triang3l): Unorm24 (rounding) output mode.
};
struct {
@@ -439,8 +448,13 @@ class SpirvShaderTranslator : public ShaderTranslator {
}
// Creates a special fragment shader without color outputs - this resets the
// state of the translator.
std::vector<uint8_t> CreateDepthOnlyFragmentShader();
// state of the translator. When depth_stencil_mode is a float24 mode, the
// shader reads gl_FragCoord.z, converts to float24, and writes the result to
// gl_FragDepth - matching the substitute pixel shader the DXBC backend uses
// when a guest draw has no pixel shader.
std::vector<uint8_t> CreateDepthOnlyFragmentShader(
Modification::DepthStencilMode depth_stencil_mode =
Modification::DepthStencilMode::kNoModifiers);
// Common functions useful not only for the translator, but also for EDRAM
// emulation via conventional render targets.
@@ -567,6 +581,26 @@ class SpirvShaderTranslator : public ShaderTranslator {
current_shader().implicit_early_z_write_allowed();
}
// Whether the current non-FSI pixel shader should convert the depth to 20e4.
bool DSV_IsWritingFloat24Depth() const {
if (edram_fragment_shader_interlock_) {
return false;
}
Modification::DepthStencilMode depth_stencil_mode =
GetSpirvShaderModification().pixel.depth_stencil_mode;
return depth_stencil_mode ==
Modification::DepthStencilMode::kFloat24Truncating ||
depth_stencil_mode ==
Modification::DepthStencilMode::kFloat24Rounding;
}
// Whether the shader runs at sample frequency - when converting depth to
// float24 from the rasterizer's own depth (not guest oDepth), each sample
// needs its own depth value for intersections to be antialiased.
bool IsSampleRate() const {
return is_pixel_shader() && DSV_IsWritingFloat24Depth() &&
!current_shader().writes_depth();
}
uint32_t GetModificationInterpolatorMask() const {
Modification modification = GetSpirvShaderModification();
return is_vertex_shader() ? modification.vertex.interpolator_mask

View File

@@ -1517,28 +1517,107 @@ void SpirvShaderTranslator::CompleteFragmentShader_DSV_DepthTo24Bit() {
output_fragment_depth_ == spv::NoResult) {
return;
}
if (!current_shader().writes_depth()) {
bool shader_writes_depth = current_shader().writes_depth();
bool is_float24 = DSV_IsWritingFloat24Depth();
assert_true(shader_writes_depth || is_float24);
// Source the depth: staged guest oDepth (already [0, 1]), or the rasterizer's
// own depth from gl_FragCoord.z remapped from host 0...0.5 back to 0...1.
spv::Id depth_value;
if (shader_writes_depth) {
depth_value =
builder_->createLoad(output_or_var_fragment_depth_, spv::NoPrecision);
} else {
assert_true(input_fragment_coordinates_ != spv::NoResult);
id_vector_temp_.clear();
id_vector_temp_.push_back(builder_->makeIntConstant(2));
spv::Id raster_z =
builder_->createLoad(builder_->createAccessChain(
spv::StorageClassInput,
input_fragment_coordinates_, id_vector_temp_),
spv::NoPrecision);
depth_value = builder_->createBinOp(spv::OpFMul, type_float_, raster_z,
builder_->makeFloatConstant(2.0f));
depth_value = builder_->createTriBuiltinCall(
type_float_, ext_inst_glsl_std_450_, GLSLstd450NClamp, depth_value,
const_float_0_, const_float_1_);
}
if (!is_float24) {
// Legacy path: shader writes oDepth, but the modification is not in float24
// mode (the host buffer may still be float24 if depth_float24_convert_in_
// pixel_shader is off - check dynamically via the system flag).
spv::Id depth_float24_flag = builder_->createBinOp(
spv::OpINotEqual, type_bool_,
builder_->createBinOp(
spv::OpBitwiseAnd, type_uint_, main_system_constant_flags_,
builder_->makeUintConstant(kSysFlag_DepthFloat24)),
const_uint_0_);
spv::Id depth_scaled =
builder_->createBinOp(spv::OpFMul, type_float_, depth_value,
builder_->makeFloatConstant(0.5f));
spv::Id depth_remapped =
builder_->createTriOp(spv::OpSelect, type_float_, depth_float24_flag,
depth_scaled, depth_value);
builder_->createStore(depth_remapped, output_fragment_depth_);
return;
}
assert_true(output_or_var_fragment_depth_ != spv::NoResult);
// The shader writes depth explicitly, for float24, need to scale it from
// guest 0...1 to host 0...0.5 to support reinterpretation round trips as
// viewport scaling doesn't apply to oDepth.
spv::Id depth_value =
builder_->createLoad(output_or_var_fragment_depth_, spv::NoPrecision);
spv::Id depth_float24_flag = builder_->createBinOp(
spv::OpINotEqual, type_bool_,
builder_->createBinOp(spv::OpBitwiseAnd, type_uint_,
main_system_constant_flags_,
builder_->makeUintConstant(kSysFlag_DepthFloat24)),
const_uint_0_);
spv::Id depth_scaled = builder_->createBinOp(
spv::OpFMul, type_float_, depth_value, builder_->makeFloatConstant(0.5f));
spv::Id depth_remapped =
builder_->createTriOp(spv::OpSelect, type_float_, depth_float24_flag,
depth_scaled, depth_value);
// Write the depth from the temporary to the system depth output.
builder_->createStore(depth_remapped, output_fragment_depth_);
// Float24 mode: statically known float24 host buffer; perform the conversion.
Modification::DepthStencilMode mode =
GetSpirvShaderModification().pixel.depth_stencil_mode;
if (mode == Modification::DepthStencilMode::kFloat24Truncating) {
// Mantissa bit-truncation, then guest 0...1 -> host 0...0.5.
spv::Id depth_uint =
builder_->createUnaryOp(spv::OpBitcast, type_uint_, depth_value);
// Representable as float24 (exponent >= -34): bit pattern >= 0x2E800000.
spv::Id representable =
builder_->createBinOp(spv::OpUGreaterThanEqual, type_bool_, depth_uint,
builder_->makeUintConstant(0x2E800000));
SpirvBuilder::IfBuilder representable_if(
representable, spv::SelectionControlDontFlattenMask, *builder_);
{
// Biased exponent: 113+ at exp -14+; 93 at exp -34.
spv::Id exponent = builder_->createTriOp(
spv::OpBitFieldUExtract, type_uint_, depth_uint,
builder_->makeUintConstant(23), builder_->makeUintConstant(8));
// trunc_bits = max(116 - exponent, 3), in signed - drops 3 mantissa bits
// at exp -14+ and 23 at exp -34. Must be signed: exponent > 116 (i.e.
// values larger than ~2^-11) makes 116 - exponent negative; an unsigned
// underflow would feed OpBitFieldInsert a Count > 32 (undefined).
spv::Id trunc_bits_signed = builder_->createBinOp(
spv::OpISub, type_int_, builder_->makeIntConstant(116),
builder_->createUnaryOp(spv::OpBitcast, type_int_, exponent));
trunc_bits_signed = builder_->createBinBuiltinCall(
type_int_, ext_inst_glsl_std_450_, GLSLstd450SMax, trunc_bits_signed,
builder_->makeIntConstant(3));
spv::Id trunc_bits = builder_->createUnaryOp(spv::OpBitcast, type_uint_,
trunc_bits_signed);
spv::Id truncated_uint =
builder_->createQuadOp(spv::OpBitFieldInsert, type_uint_, depth_uint,
builder_->makeUintConstant(0),
builder_->makeUintConstant(0), trunc_bits);
spv::Id truncated_f32 =
builder_->createUnaryOp(spv::OpBitcast, type_float_, truncated_uint);
spv::Id remapped =
builder_->createBinOp(spv::OpFMul, type_float_, truncated_f32,
builder_->makeFloatConstant(0.5f));
builder_->createStore(remapped, output_fragment_depth_);
}
representable_if.makeBeginElse();
{
// Not representable - zero.
builder_->createStore(const_float_0_, output_fragment_depth_);
}
representable_if.makeEndIf();
} else {
// kFloat24Rounding: round-trip through 20e4 (round to nearest even), with
// the 0...0.5 host remap baked in via remap_to_0_to_0_5 on Depth20e4To32.
spv::Id f24_uint = PreClampedDepthTo20e4(*builder_, depth_value, true,
false, ext_inst_glsl_std_450_);
spv::Id depth_f32 = Depth20e4To32(*builder_, f24_uint, 0, true, false,
ext_inst_glsl_std_450_);
builder_->createStore(depth_f32, output_fragment_depth_);
}
}
spv::Id SpirvShaderTranslator::LoadMsaaSamplesFromFlags() {

View File

@@ -2441,6 +2441,8 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
SpirvShaderTranslator::Modification pixel_shader_modification;
VulkanShader::VulkanTranslation* vertex_shader_translation;
VulkanShader::VulkanTranslation* pixel_shader_translation;
uint32_t normalized_color_mask;
reg::RB_DEPTHCONTROL normalized_depth_control;
// Two iterations because a submission (even the current one - in which case
// it needs to be ended, and a new one must be started) may need to be awaited
@@ -2475,6 +2477,14 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
return false;
}
normalized_depth_control = draw_util::GetNormalizedDepthControl(regs);
// Compute which color render targets are used.
normalized_color_mask =
pixel_shader ? draw_util::GetNormalizedColorMask(
regs, pixel_shader->writes_color_targets())
: 0;
// Shader modifications.
vertex_shader_modification =
pipeline_cache_->GetCurrentVertexShaderModification(
@@ -2482,7 +2492,8 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
interpolator_mask, ps_param_gen_pos != UINT32_MAX);
pixel_shader_modification =
pixel_shader ? pipeline_cache_->GetCurrentPixelShaderModification(
*pixel_shader, interpolator_mask, ps_param_gen_pos)
*pixel_shader, interpolator_mask, ps_param_gen_pos,
normalized_depth_control, normalized_color_mask)
: SpirvShaderTranslator::Modification(0);
// Translate the shaders now to obtain the sampler bindings.
@@ -2571,12 +2582,6 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
}
// Set up the render targets - this may perform dispatches and draws.
reg::RB_DEPTHCONTROL normalized_depth_control =
draw_util::GetNormalizedDepthControl(regs);
uint32_t normalized_color_mask =
pixel_shader ? draw_util::GetNormalizedColorMask(
regs, pixel_shader->writes_color_targets())
: 0;
if (!render_target_cache_->Update(is_rasterization_done,
normalized_depth_control,
normalized_color_mask, *vertex_shader)) {
@@ -2699,17 +2704,20 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
// into system constants.
UpdateZPDScale(draw_resolution_scale_x * draw_resolution_scale_y);
draw_util::GetViewportInfoArgs gviargs{};
gviargs.Setup(draw_resolution_scale_x, draw_resolution_scale_y,
draw_resolution_scale_x > 1
? texture_cache_->draw_resolution_scale_x_divisor()
: divisors::MagicDiv(1),
draw_resolution_scale_y > 1
? texture_cache_->draw_resolution_scale_y_divisor()
: divisors::MagicDiv(1),
false, device_properties.maxViewportDimensions[0],
device_properties.maxViewportDimensions[1], true,
normalized_depth_control, false, host_render_targets_used,
pixel_shader && pixel_shader->writes_depth());
gviargs.Setup(
draw_resolution_scale_x, draw_resolution_scale_y,
draw_resolution_scale_x > 1
? texture_cache_->draw_resolution_scale_x_divisor()
: divisors::MagicDiv(1),
draw_resolution_scale_y > 1
? texture_cache_->draw_resolution_scale_y_divisor()
: divisors::MagicDiv(1),
false, device_properties.maxViewportDimensions[0],
device_properties.maxViewportDimensions[1], true,
normalized_depth_control,
host_render_targets_used &&
render_target_cache_->depth_float24_convert_in_pixel_shader(),
host_render_targets_used, pixel_shader && pixel_shader->writes_depth());
gviargs.SetupRegisterValues(regs);
draw_util::GetHostViewportInfo(&gviargs, viewport_info);

View File

@@ -108,6 +108,32 @@ bool VulkanPipelineCache::Initialize() {
}
}
// Substitute fragment shaders for guest depth-only draws when in-PS float24
// conversion is active - keep the depth buffer's encoding consistent with
// PS-converted draws (matches the DXBC backend's
// float24_{truncate,round}_ps).
if (render_target_cache_.depth_float24_convert_in_pixel_shader()) {
using DepthStencilMode =
SpirvShaderTranslator::Modification::DepthStencilMode;
auto build = [&](DepthStencilMode mode, VkShaderModule& out) -> bool {
std::vector<uint8_t> code =
shader_translator_->CreateDepthOnlyFragmentShader(mode);
out = ui::vulkan::util::CreateShaderModule(
vulkan_device, reinterpret_cast<const uint32_t*>(code.data()),
code.size());
return out != VK_NULL_HANDLE;
};
if (!build(DepthStencilMode::kFloat24Truncating,
float24_truncate_fragment_shader_) ||
!build(DepthStencilMode::kFloat24Rounding,
float24_round_fragment_shader_)) {
XELOGE(
"VulkanPipelineCache: Failed to create the float24 substitute "
"depth-only fragment shaders");
return false;
}
}
// Create tessellation shaders if tessellation is supported.
if (vulkan_device->properties().tessellationShader) {
// Vertex shaders for tessellation.
@@ -287,6 +313,10 @@ void VulkanPipelineCache::Shutdown() {
// Destroy all internal shaders.
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyShaderModule, device,
depth_only_fragment_shader_);
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyShaderModule, device,
float24_truncate_fragment_shader_);
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyShaderModule, device,
float24_round_fragment_shader_);
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyShaderModule, device,
placeholder_pixel_shader_);
// Destroy tessellation shaders.
@@ -395,8 +425,9 @@ VulkanPipelineCache::GetCurrentVertexShaderModification(
SpirvShaderTranslator::Modification
VulkanPipelineCache::GetCurrentPixelShaderModification(
const Shader& shader, uint32_t interpolator_mask,
uint32_t param_gen_pos) const {
const Shader& shader, uint32_t interpolator_mask, uint32_t param_gen_pos,
reg::RB_DEPTHCONTROL normalized_depth_control,
uint32_t normalized_color_mask) const {
assert_true(shader.type() == xenos::ShaderType::kPixel);
assert_true(shader.is_ucode_analyzed());
const auto& regs = register_file_;
@@ -434,10 +465,18 @@ VulkanPipelineCache::GetCurrentPixelShaderModification(
using DepthStencilMode =
SpirvShaderTranslator::Modification::DepthStencilMode;
if (shader.implicit_early_z_write_allowed() &&
(!shader.writes_color_target(0) ||
!draw_util::DoesCoverageDependOnAlpha(
regs.Get<reg::RB_COLORCONTROL>()))) {
if (render_target_cache_.depth_float24_convert_in_pixel_shader() &&
normalized_depth_control.z_enable &&
regs.Get<reg::RB_DEPTH_INFO>().depth_format ==
xenos::DepthRenderTargetFormat::kD24FS8) {
modification.pixel.depth_stencil_mode =
render_target_cache_.depth_float24_round()
? DepthStencilMode::kFloat24Rounding
: DepthStencilMode::kFloat24Truncating;
} else if (shader.implicit_early_z_write_allowed() &&
(!shader.writes_color_target(0) ||
!draw_util::DoesCoverageDependOnAlpha(
regs.Get<reg::RB_COLORCONTROL>()))) {
modification.pixel.depth_stencil_mode = DepthStencilMode::kEarlyHint;
} else {
modification.pixel.depth_stencil_mode = DepthStencilMode::kNoModifiers;
@@ -2630,6 +2669,19 @@ bool VulkanPipelineCache::EnsurePipelineCreated(
} else {
if (edram_fragment_shader_interlock) {
shader_stage_fragment.module = depth_only_fragment_shader_;
} else if (render_target_cache_.depth_float24_convert_in_pixel_shader() &&
(description.depth_write_enable ||
description.depth_compare_op !=
xenos::CompareFunction::kAlways) &&
(description.render_pass_key.depth_and_color_used & 0b1) &&
description.render_pass_key.depth_format ==
xenos::DepthRenderTargetFormat::kD24FS8) {
// No guest pixel shader, but depth matters and the host buffer is
// float24 - bind a substitute that converts gl_FragCoord.z so the
// depth buffer encoding stays consistent with PS-converted draws.
shader_stage_fragment.module = render_target_cache_.depth_float24_round()
? float24_round_fragment_shader_
: float24_truncate_fragment_shader_;
}
}
if (shader_stage_fragment.module == VK_NULL_HANDLE) {

View File

@@ -132,8 +132,9 @@ class VulkanPipelineCache {
Shader::HostVertexShaderType host_vertex_shader_type,
uint32_t interpolator_mask, bool ps_param_gen_used) const;
SpirvShaderTranslator::Modification GetCurrentPixelShaderModification(
const Shader& shader, uint32_t interpolator_mask,
uint32_t param_gen_pos) const;
const Shader& shader, uint32_t interpolator_mask, uint32_t param_gen_pos,
reg::RB_DEPTHCONTROL normalized_depth_control,
uint32_t normalized_color_mask) const;
bool EnsureShadersTranslated(VulkanShader::VulkanTranslation* vertex_shader,
VulkanShader::VulkanTranslation* pixel_shader);
@@ -433,6 +434,13 @@ class VulkanPipelineCache {
// shader interlock when no Xenos pixel shader provided.
VkShaderModule depth_only_fragment_shader_ = VK_NULL_HANDLE;
// Substitute depth-only pixel shaders that perform float24 conversion of the
// rasterizer's depth, bound for guest depth-only draws when in-PS float24
// conversion is active and the depth buffer is D24FS8. Mirrors the DXBC
// backend's float24_{truncate,round}_ps.
VkShaderModule float24_truncate_fragment_shader_ = VK_NULL_HANDLE;
VkShaderModule float24_round_fragment_shader_ = VK_NULL_HANDLE;
// Placeholder pixel shader for pipeline hot-swap to reduce stutter.
// Outputs transparent black while the real shader compiles in background.
VkShaderModule placeholder_pixel_shader_ = VK_NULL_HANDLE;

View File

@@ -570,6 +570,12 @@ bool VulkanRenderTargetCache::Initialize(uint32_t shared_memory_binding_count) {
kGammaUnorm16Features) == kGammaUnorm16Features;
depth_float24_round_ = cvars::depth_float24_round;
// In-PS conversion requires per-sample shading under MSAA for intersections
// to antialias; without sampleRateShading, fall back to transfer-time
// conversion so the host/PS encoding stays consistent across all draws.
depth_float24_convert_in_pixel_shader_ =
cvars::depth_float24_convert_in_pixel_shader &&
device_properties.sampleRateShading;
// Host depth storing pipeline layout.
VkDescriptorSetLayout host_depth_store_descriptor_set_layouts[] = {
@@ -773,8 +779,11 @@ bool VulkanRenderTargetCache::Initialize(uint32_t shared_memory_binding_count) {
// Piecewise linear gamma is 8-bit with programmable blending.
gamma_render_target_as_unorm16_ = false;
// Always true float24 depth rounded to the nearest even.
// Always true float24 depth rounded to the nearest even, converted in the
// shader (FSI ignores depth_float24_convert_in_pixel_shader, but set it for
// parity with the host render target path).
depth_float24_round_ = true;
depth_float24_convert_in_pixel_shader_ = true;
// The pipeline layout and the pipelines for clearing the EDRAM buffer in
// resolves.
@@ -2105,12 +2114,14 @@ RenderTargetCache::RenderTarget* VulkanRenderTargetCache::CreateRenderTarget(
bool VulkanRenderTargetCache::IsHostDepthEncodingDifferent(
xenos::DepthRenderTargetFormat format) const {
// TODO(Triang3l): Conversion directly in shaders.
switch (format) {
case xenos::DepthRenderTargetFormat::kD24S8:
return !depth_unorm24_vulkan_format_supported();
case xenos::DepthRenderTargetFormat::kD24FS8:
return true;
// When converting in the pixel shader, the host float32 depth already
// holds float24-grid values, so it's the canonical encoding and the
// separate host depth tracking isn't needed.
return !depth_float24_convert_in_pixel_shader();
}
return false;
}
@@ -3605,8 +3616,10 @@ VkShaderModule VulkanRenderTargetCache::GetTransferShader(
} break;
case xenos::DepthRenderTargetFormat::kD24FS8: {
depth24 = SpirvShaderTranslator::PreClampedDepthTo20e4(
builder, source_depth_float[i], depth_float24_round(), true,
ext_inst_glsl_std_450);
builder, source_depth_float[i],
!depth_float24_convert_in_pixel_shader() &&
depth_float24_round(),
true, ext_inst_glsl_std_450);
} break;
}
// Merge depth and stencil.
@@ -3911,8 +3924,10 @@ VkShaderModule VulkanRenderTargetCache::GetTransferShader(
} break;
case xenos::DepthRenderTargetFormat::kD24FS8: {
packed = SpirvShaderTranslator::PreClampedDepthTo20e4(
builder, source_depth_float[0], depth_float24_round(), true,
ext_inst_glsl_std_450);
builder, source_depth_float[0],
!depth_float24_convert_in_pixel_shader() &&
depth_float24_round(),
true, ext_inst_glsl_std_450);
} break;
}
if (mode.output == TransferOutput::kDepth) {
@@ -4391,8 +4406,10 @@ VkShaderModule VulkanRenderTargetCache::GetTransferShader(
} break;
case xenos::DepthRenderTargetFormat::kD24FS8: {
host_depth24 = SpirvShaderTranslator::PreClampedDepthTo20e4(
builder, host_depth32, depth_float24_round(), true,
ext_inst_glsl_std_450);
builder, host_depth32,
!depth_float24_convert_in_pixel_shader() &&
depth_float24_round(),
true, ext_inst_glsl_std_450);
} break;
}
assert_true(host_depth24 != spv::NoResult);
@@ -6109,8 +6126,9 @@ VkPipeline VulkanRenderTargetCache::GetDumpPipeline(DumpPipelineKey key) {
} break;
case xenos::DepthRenderTargetFormat::kD24FS8: {
packed[0] = SpirvShaderTranslator::PreClampedDepthTo20e4(
builder, source_depth32, depth_float24_round(), true,
ext_inst_glsl_std_450);
builder, source_depth32,
!depth_float24_convert_in_pixel_shader() && depth_float24_round(),
true, ext_inst_glsl_std_450);
} break;
}
packed[0] = builder.createQuadOp(

View File

@@ -161,6 +161,9 @@ class VulkanRenderTargetCache final : public RenderTargetCache {
return depth_unorm24_vulkan_format_supported_;
}
bool depth_float24_round() const { return depth_float24_round_; }
bool depth_float24_convert_in_pixel_shader() const {
return depth_float24_convert_in_pixel_shader_;
}
bool msaa_2x_attachments_supported() const {
return msaa_2x_attachments_supported_;
@@ -891,6 +894,7 @@ class VulkanRenderTargetCache final : public RenderTargetCache {
bool depth_unorm24_vulkan_format_supported_ = false;
bool depth_float24_round_ = false;
bool depth_float24_convert_in_pixel_shader_ = false;
bool msaa_2x_attachments_supported_ = false;
bool msaa_2x_no_attachments_supported_ = false;