diff --git a/thirdparty/rexglue-sdk/include/native/ui/d3d12/d3d12_provider.h b/thirdparty/rexglue-sdk/include/native/ui/d3d12/d3d12_provider.h index ca20c1de..58aed57f 100644 --- a/thirdparty/rexglue-sdk/include/native/ui/d3d12/d3d12_provider.h +++ b/thirdparty/rexglue-sdk/include/native/ui/d3d12/d3d12_provider.h @@ -92,6 +92,12 @@ class D3D12Provider : public GraphicsProvider { D3D12_RESOURCE_BINDING_TIER GetResourceBindingTier() const { return resource_binding_tier_; } D3D12_TILED_RESOURCES_TIER GetTiledResourcesTier() const { return tiled_resources_tier_; } bool AreUnalignedBlockTexturesSupported() const { return unaligned_block_textures_supported_; } + // D3D12_BLEND_ALPHA_FACTOR / INV_ALPHA_FACTOR are an OPTIONAL feature: the + // docs require D3D12_FEATURE_DATA_D3D12_OPTIONS13::AlphaBlendFactorSupported + // to be TRUE before either may be used. Using them without checking gets the + // whole pipeline rejected, and the draw disappears with it. False when the + // runtime is too old to answer, which is the safe reading. + bool IsAlphaBlendFactorSupported() const { return alpha_blend_factor_supported_; } uint32_t GetVirtualAddressBitsPerResource() const { return virtual_address_bits_per_resource_; } // Proxies for DirectX functions since they are loaded dynamically. @@ -167,6 +173,7 @@ class D3D12Provider : public GraphicsProvider { bool ps_specified_stencil_reference_supported_; bool rasterizer_ordered_views_supported_; bool unaligned_block_textures_supported_; + bool alpha_blend_factor_supported_; }; } // namespace rex::ui::d3d12 diff --git a/thirdparty/rexglue-sdk/include/rex/graphics/pipeline/shader/dxbc_translator.h b/thirdparty/rexglue-sdk/include/rex/graphics/pipeline/shader/dxbc_translator.h index 9a880d18..9c94feb8 100644 --- a/thirdparty/rexglue-sdk/include/rex/graphics/pipeline/shader/dxbc_translator.h +++ b/thirdparty/rexglue-sdk/include/rex/graphics/pipeline/shader/dxbc_translator.h @@ -111,7 +111,7 @@ class DxbcShaderTranslator : public ShaderTranslator { // If anything in this is structure is changed in a way not compatible with // the previous layout, invalidate the pipeline storages by increasing this // version number (0xYYYYMMDD)! - static constexpr uint32_t kVersion = 0x20260410; + static constexpr uint32_t kVersion = 0x20260809; enum class DepthStencilMode : uint32_t { kNoModifiers, @@ -176,6 +176,18 @@ class DxbcShaderTranslator : public ShaderTranslator { uint32_t dynamic_addressable_register_count : 8; // Non-ROV - depth / stencil output mode. DepthStencilMode depth_stencil_mode : 2; + // Non-ROV - write the blend constant's alpha into the alpha of colour + // output 0 instead of what the guest shader produced. + // + // The colour blend equation has no way of its own to reach the blend + // constant's alpha, and D3D12's ALPHA_FACTOR family - which exists for + // exactly that - is an optional feature some devices do not have. Putting + // the value in the alpha output lets INV_SRC_ALPHA stand in for + // ONE_MINUS_CONSTANT_ALPHA exactly, using only always-available factors. + // + // Only set when the alpha equation does not read the alpha output, so + // overwriting it cannot change the alpha result. + uint32_t alpha_output_is_blend_constant : 1; } pixel; explicit Modification(uint64_t modification_value = 0) : value(modification_value) { diff --git a/thirdparty/rexglue-sdk/src/graphics/d3d12/command_processor.cpp b/thirdparty/rexglue-sdk/src/graphics/d3d12/command_processor.cpp index c335acc7..ad302a67 100644 --- a/thirdparty/rexglue-sdk/src/graphics/d3d12/command_processor.cpp +++ b/thirdparty/rexglue-sdk/src/graphics/d3d12/command_processor.cpp @@ -4702,6 +4702,19 @@ void D3D12CommandProcessor::UpdateSystemConstantValues( system_constants_.edram_blend_constant[3] = regs.Get(XE_GPU_REG_RB_BLEND_ALPHA); } + // The blend constant is read by the pixel shader on the RTV path too when + // alpha_output_is_blend_constant is set, so upload it regardless of path. + // Four dirty-tracked floats; a shader reading a stale constant would cost + // far more than the upload does. + dirty |= system_constants_.edram_blend_constant[0] != regs.Get(XE_GPU_REG_RB_BLEND_RED); + system_constants_.edram_blend_constant[0] = regs.Get(XE_GPU_REG_RB_BLEND_RED); + dirty |= system_constants_.edram_blend_constant[1] != regs.Get(XE_GPU_REG_RB_BLEND_GREEN); + system_constants_.edram_blend_constant[1] = regs.Get(XE_GPU_REG_RB_BLEND_GREEN); + dirty |= system_constants_.edram_blend_constant[2] != regs.Get(XE_GPU_REG_RB_BLEND_BLUE); + system_constants_.edram_blend_constant[2] = regs.Get(XE_GPU_REG_RB_BLEND_BLUE); + dirty |= system_constants_.edram_blend_constant[3] != regs.Get(XE_GPU_REG_RB_BLEND_ALPHA); + system_constants_.edram_blend_constant[3] = regs.Get(XE_GPU_REG_RB_BLEND_ALPHA); + cbuffer_binding_system_.up_to_date &= !dirty; } diff --git a/thirdparty/rexglue-sdk/src/graphics/d3d12/pipeline_cache.cpp b/thirdparty/rexglue-sdk/src/graphics/d3d12/pipeline_cache.cpp index 4d821996..4a844046 100644 --- a/thirdparty/rexglue-sdk/src/graphics/d3d12/pipeline_cache.cpp +++ b/thirdparty/rexglue-sdk/src/graphics/d3d12/pipeline_cache.cpp @@ -61,8 +61,66 @@ REXCVAR_DEFINE_INT32(d3d12_pipeline_creation_threads, -1, "GPU/D3D12", REXCVAR_DEFINE_BOOL(d3d12_tessellation_wireframe, false, "GPU/D3D12", "Render tessellation as wireframe"); +REXCVAR_DEFINE_BOOL(d3d12_constant_alpha_blend, true, "GPU/D3D12", + "Use the D3D12 constant-alpha blend factors where the device reports " + "support for them. Turning this off forces the substitute encoding even " + "on a device that allows them, which is the only way to exercise that " + "path for testing; devices without support always use it regardless") + .lifecycle(rex::cvar::Lifecycle::kRequiresRestart); + namespace rex::graphics::d3d12 { +// Whether the D3D12 constant-alpha blend factors may be emitted: the device +// must report AlphaBlendFactorSupported - they are an optional feature, and +// using them without it makes CreateGraphicsPipelineState reject the pipeline +// outright - and the cvar must not be forcing the substitute for testing. +static bool UseConstantAlphaBlendFactors(const D3D12CommandProcessor& command_processor) { + return command_processor.GetD3D12Provider().IsAlphaBlendFactorSupported() && + REXCVAR_GET(d3d12_constant_alpha_blend); +} + +// Whether this draw may carry the blend constant's alpha in the pixel shader's +// alpha output so INV_SRC_ALPHA can stand in for ONE_MINUS_CONSTANT_ALPHA. +// +// Needed only when a COLOUR slot asks for the constant's alpha (guest 14/15) - +// the colour equation is the one with no other route to it. Permitted only when +// nothing else reads the alpha the shader writes, because the route overwrites +// it: no slot anywhere may use a source-alpha factor (guest 6, 7 and the +// saturating 16 read it; in the alpha equation guest 4 and 5 collapse onto it +// too), and neither the alpha test nor alpha-to-mask may be enabled. +// +// Scans all four blend controls rather than only the bound ones. That can only +// withhold the route, never grant it wrongly, which is the safe direction. +static bool DrawNeedsBlendConstantAlphaInOutput(const RegisterFile& regs) { + auto color_control = regs.Get(); + if (color_control.alpha_test_enable || color_control.alpha_to_mask_enable) { + return false; + } + bool colour_slot_wants_constant_alpha = false; + for (uint32_t i = 0; i < xenos::kMaxColorRenderTargets; ++i) { + auto blendcontrol = + regs.Get(reg::RB_BLENDCONTROL::rt_register_indices[i]); + uint32_t colour_factors[] = {uint32_t(blendcontrol.color_srcblend), + uint32_t(blendcontrol.color_destblend)}; + uint32_t alpha_factors[] = {uint32_t(blendcontrol.alpha_srcblend), + uint32_t(blendcontrol.alpha_destblend)}; + for (uint32_t factor : colour_factors) { + if (factor == 14 || factor == 15) { + colour_slot_wants_constant_alpha = true; + } + if (factor == 6 || factor == 7 || factor == 16) { + return false; + } + } + for (uint32_t factor : alpha_factors) { + if (factor == 4 || factor == 5 || factor == 6 || factor == 7 || factor == 16) { + return false; + } + } + } + return colour_slot_wants_constant_alpha; +} + // Generated with `xb buildshaders`. namespace shaders { #include "../shaders/bytecode/d3d12_5_1/adaptive_quad_hs.h" @@ -872,6 +930,13 @@ DxbcShaderTranslator::Modification PipelineCache::GetCurrentPixelShaderModificat modification.pixel.param_gen_point = 0; } + modification.pixel.alpha_output_is_blend_constant = + (!UseConstantAlphaBlendFactors(command_processor_) && + render_target_cache_.GetPath() == RenderTargetCache::Path::kHostRenderTargets && + DrawNeedsBlendConstantAlphaInOutput(regs)) + ? 1 + : 0; + if (render_target_cache_.GetPath() == RenderTargetCache::Path::kHostRenderTargets) { using DepthStencilMode = DxbcShaderTranslator::Modification::DepthStencilMode; if (render_target_cache_.depth_float24_convert_in_pixel_shader() && @@ -1534,10 +1599,13 @@ bool PipelineCache::GetCurrentStateDescription( /* 12 */ PipelineBlendFactor::kBlendFactor, // ONE_MINUS_CONSTANT_COLOR /* 13 */ PipelineBlendFactor::kInvBlendFactor, - // CONSTANT_ALPHA - uses the constant's ALPHA, not RGB. - /* 14 */ PipelineBlendFactor::kBlendFactorAlpha, - // ONE_MINUS_CONSTANT_ALPHA - uses 1 - constant ALPHA, not 1 - RGB. - /* 15 */ PipelineBlendFactor::kInvBlendFactorAlpha, + // CONSTANT_ALPHA / ONE_MINUS_CONSTANT_ALPHA. This is the ALPHA blend + // equation, where D3D12's plain BLEND_FACTOR already takes the + // constant's alpha component - ALPHA_FACTOR computes the same value + // here. Use the always-available factor so the alpha slots never depend + // on an optional feature; only the colour slots genuinely need one. + /* 14 */ PipelineBlendFactor::kBlendFactor, + /* 15 */ PipelineBlendFactor::kInvBlendFactor, /* 16 */ PipelineBlendFactor::kSrcAlphaSat, }; // While it's okay to specify fewer render targets in the pipeline state @@ -1552,6 +1620,32 @@ bool PipelineCache::GetCurrentStateDescription( // multisampled render targets bound (happens in 4D5307E6 main menu). // TODO(Triang3l): Investigate interaction of OMSetRenderTargets with // non-null depth and DSVFormat DXGI_FORMAT_UNKNOWN in the same case. + // The colour blend equation cannot reach the blend constant's alpha on its + // own, so the Xenos CONSTANT_ALPHA / ONE_MINUS_CONSTANT_ALPHA factors + // (14/15) need D3D12's optional ALPHA_FACTOR family there. Where that is + // unavailable the pixel shader carries the constant's alpha in its alpha + // output instead, and SRC_ALPHA / INV_SRC_ALPHA reproduce them exactly. If + // even that route is closed, fall back to the constant-colour family - + // wrong, but it keeps the draw. + const bool substitute_constant_alpha = !UseConstantAlphaBlendFactors(command_processor_); + const bool alpha_via_output = + substitute_constant_alpha && DrawNeedsBlendConstantAlphaInOutput(regs); + auto map_colour_blend_factor = [substitute_constant_alpha, + alpha_via_output](PipelineBlendFactor factor) { + if (!substitute_constant_alpha) { + return factor; + } + switch (factor) { + case PipelineBlendFactor::kBlendFactorAlpha: + return alpha_via_output ? PipelineBlendFactor::kSrcAlpha + : PipelineBlendFactor::kBlendFactor; + case PipelineBlendFactor::kInvBlendFactorAlpha: + return alpha_via_output ? PipelineBlendFactor::kInvSrcAlpha + : PipelineBlendFactor::kInvBlendFactor; + default: + return factor; + } + }; for (uint32_t i = 0; i < 4; ++i) { if (!(bound_depth_and_color_render_target_bits & (uint32_t(1) << (1 + i)))) { continue; @@ -1565,8 +1659,10 @@ bool PipelineCache::GetCurrentStateDescription( if (rt.write_mask) { auto blendcontrol = regs.Get(reg::RB_BLENDCONTROL::rt_register_indices[i]); - rt.src_blend = kBlendFactorMap[uint32_t(blendcontrol.color_srcblend)]; - rt.dest_blend = kBlendFactorMap[uint32_t(blendcontrol.color_destblend)]; + rt.src_blend = + map_colour_blend_factor(kBlendFactorMap[uint32_t(blendcontrol.color_srcblend)]); + rt.dest_blend = + map_colour_blend_factor(kBlendFactorMap[uint32_t(blendcontrol.color_destblend)]); rt.blend_op = blendcontrol.color_comb_fcn; rt.src_blend_alpha = kBlendFactorAlphaMap[uint32_t(blendcontrol.alpha_srcblend)]; rt.dest_blend_alpha = kBlendFactorAlphaMap[uint32_t(blendcontrol.alpha_destblend)]; diff --git a/thirdparty/rexglue-sdk/src/graphics/pipeline/shader/dxbc_translator_om.cpp b/thirdparty/rexglue-sdk/src/graphics/pipeline/shader/dxbc_translator_om.cpp index a7d508a9..2d464aee 100644 --- a/thirdparty/rexglue-sdk/src/graphics/pipeline/shader/dxbc_translator_om.cpp +++ b/thirdparty/rexglue-sdk/src/graphics/pipeline/shader/dxbc_translator_om.cpp @@ -1563,6 +1563,17 @@ void DxbcShaderTranslator::CompletePixelShader_WriteToRTVs() { } a_.OpEndIf(); } + if (i == 0 && GetDxbcShaderModification().pixel.alpha_output_is_blend_constant) { + // Hand the blend constant's alpha to the output merger through the alpha + // output, so INV_SRC_ALPHA can express ONE_MINUS_CONSTANT_ALPHA exactly + // on devices without the constant-alpha blend factors. Safe only because + // this modification is set exclusively for draws whose alpha equation + // ignores the alpha output - see PixelShaderModification. + a_.OpMov(dxbc::Dest::R(system_temp_color, 0b1000), + LoadSystemConstant(SystemConstants::Index::kEdramBlendConstant, + offsetof(SystemConstants, edram_blend_constant), + dxbc::Src::kWWWW)); + } // Copy the color from a readable temp register to an output register. a_.OpMov(dxbc::Dest::O(i), dxbc::Src::R(system_temp_color)); } diff --git a/thirdparty/rexglue-sdk/src/native/ui/d3d12/d3d12_provider.cpp b/thirdparty/rexglue-sdk/src/native/ui/d3d12/d3d12_provider.cpp index 7070f28d..375b3e3f 100644 --- a/thirdparty/rexglue-sdk/src/native/ui/d3d12/d3d12_provider.cpp +++ b/thirdparty/rexglue-sdk/src/native/ui/d3d12/d3d12_provider.cpp @@ -435,6 +435,16 @@ bool D3D12Provider::Initialize() { device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS8, &options8, sizeof(options8)))) { unaligned_block_textures_supported_ = bool(options8.UnalignedBlockTexturesSupported); } + // Optional as of the Direct3D 12 Agility SDK: the constant-alpha blend + // factors may not be used at all unless this says so. Left false when the + // query is unavailable - an older runtime that cannot answer is not a + // licence to assume yes. + alpha_blend_factor_supported_ = false; + D3D12_FEATURE_DATA_D3D12_OPTIONS13 options13; + if (SUCCEEDED(device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS13, &options13, + sizeof(options13)))) { + alpha_blend_factor_supported_ = bool(options13.AlphaBlendFactorSupported); + } virtual_address_bits_per_resource_ = 0; D3D12_FEATURE_DATA_GPU_VIRTUAL_ADDRESS_SUPPORT virtual_address_support; if (SUCCEEDED(device->CheckFeatureSupport(D3D12_FEATURE_GPU_VIRTUAL_ADDRESS_SUPPORT, @@ -452,13 +462,15 @@ bool D3D12Provider::Initialize() { "* Rasterizer-ordered views: {}\n" "* Resource binding: tier {}\n" "* Tiled resources: tier {}\n" - "* Unaligned block-compressed textures: {}", + "* Unaligned block-compressed textures: {}\n" + "* Constant-alpha blend factors (ALPHA_FACTOR): {}", virtual_address_bits_per_resource_, (heap_flag_create_not_zeroed_ & D3D12_HEAP_FLAG_CREATE_NOT_ZEROED) ? "yes" : "no", ps_specified_stencil_reference_supported_ ? "yes" : "no", uint32_t(programmable_sample_positions_tier_), rasterizer_ordered_views_supported_ ? "yes" : "no", uint32_t(resource_binding_tier_), - uint32_t(tiled_resources_tier_), unaligned_block_textures_supported_ ? "yes" : "no"); + uint32_t(tiled_resources_tier_), unaligned_block_textures_supported_ ? "yes" : "no", + alpha_blend_factor_supported_ ? "yes" : "no"); // Get the graphics analysis interface, will silently fail if PIX is not // attached.