[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:
committed by
Radosław Gliński
parent
947075f880
commit
2a6e9f4e2a
@@ -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
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user