/** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2018 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #include "xenia/gpu/dxbc_shader_translator.h" #include "xenia/base/assert.h" #include "xenia/base/math.h" namespace xe { namespace gpu { using namespace ucode; void DxbcShaderTranslator::ROV_GetColorFormatSystemConstants( xenos::ColorRenderTargetFormat format, uint32_t write_mask, float& clamp_rgb_low, float& clamp_alpha_low, float& clamp_rgb_high, float& clamp_alpha_high, uint32_t& keep_mask_low, uint32_t& keep_mask_high) { keep_mask_low = keep_mask_high = 0; switch (format) { case xenos::ColorRenderTargetFormat::k_8_8_8_8: case xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA: { clamp_rgb_low = clamp_alpha_low = 0.0f; clamp_rgb_high = clamp_alpha_high = 1.0f; for (uint32_t i = 0; i < 4; ++i) { if (!(write_mask & (1 << i))) { keep_mask_low |= uint32_t(0xFF) << (i * 8); } } } break; case xenos::ColorRenderTargetFormat::k_2_10_10_10: case xenos::ColorRenderTargetFormat::k_2_10_10_10_AS_10_10_10_10: { clamp_rgb_low = clamp_alpha_low = 0.0f; clamp_rgb_high = clamp_alpha_high = 1.0f; for (uint32_t i = 0; i < 3; ++i) { if (!(write_mask & (1 << i))) { keep_mask_low |= uint32_t(0x3FF) << (i * 10); } } if (!(write_mask & 0b1000)) { keep_mask_low |= uint32_t(3) << 30; } } break; case xenos::ColorRenderTargetFormat::k_2_10_10_10_FLOAT: case xenos::ColorRenderTargetFormat::k_2_10_10_10_FLOAT_AS_16_16_16_16: { clamp_rgb_low = clamp_alpha_low = 0.0f; clamp_rgb_high = 31.875f; clamp_alpha_high = 1.0f; for (uint32_t i = 0; i < 3; ++i) { if (!(write_mask & (1 << i))) { keep_mask_low |= uint32_t(0x3FF) << (i * 10); } } if (!(write_mask & 0b1000)) { keep_mask_low |= uint32_t(3) << 30; } } break; case xenos::ColorRenderTargetFormat::k_16_16: case xenos::ColorRenderTargetFormat::k_16_16_16_16: // Alpha clamping affects blending source, so it's non-zero for alpha for // k_16_16 (the render target is fixed-point). There's one deviation from // how Direct3D 11.3 functional specification defines SNorm conversion // (NaN should be 0, not the lowest negative number), but NaN handling in // output shouldn't be very important. clamp_rgb_low = clamp_alpha_low = -32.0f; clamp_rgb_high = clamp_alpha_high = 32.0f; if (!(write_mask & 0b0001)) { keep_mask_low |= 0xFFFFu; } if (!(write_mask & 0b0010)) { keep_mask_low |= 0xFFFF0000u; } if (format == xenos::ColorRenderTargetFormat::k_16_16_16_16) { if (!(write_mask & 0b0100)) { keep_mask_high |= 0xFFFFu; } if (!(write_mask & 0b1000)) { keep_mask_high |= 0xFFFF0000u; } } else { write_mask &= 0b0011; } break; case xenos::ColorRenderTargetFormat::k_16_16_FLOAT: case xenos::ColorRenderTargetFormat::k_16_16_16_16_FLOAT: // No NaNs on the Xbox 360 GPU, though can't use the extended range with // f32tof16. clamp_rgb_low = clamp_alpha_low = -65504.0f; clamp_rgb_high = clamp_alpha_high = 65504.0f; if (!(write_mask & 0b0001)) { keep_mask_low |= 0xFFFFu; } if (!(write_mask & 0b0010)) { keep_mask_low |= 0xFFFF0000u; } if (format == xenos::ColorRenderTargetFormat::k_16_16_16_16_FLOAT) { if (!(write_mask & 0b0100)) { keep_mask_high |= 0xFFFFu; } if (!(write_mask & 0b1000)) { keep_mask_high |= 0xFFFF0000u; } } else { write_mask &= 0b0011; } break; case xenos::ColorRenderTargetFormat::k_32_FLOAT: // No clamping - let min/max always pick the original value. clamp_rgb_low = clamp_alpha_low = clamp_rgb_high = clamp_alpha_high = std::nanf(""); write_mask &= 0b0001; if (!(write_mask & 0b0001)) { keep_mask_low = ~uint32_t(0); } break; case xenos::ColorRenderTargetFormat::k_32_32_FLOAT: // No clamping - let min/max always pick the original value. clamp_rgb_low = clamp_alpha_low = clamp_rgb_high = clamp_alpha_high = std::nanf(""); write_mask &= 0b0011; if (!(write_mask & 0b0001)) { keep_mask_low = ~uint32_t(0); } if (!(write_mask & 0b0010)) { keep_mask_high = ~uint32_t(0); } break; default: assert_unhandled_case(format); // Disable invalid render targets. write_mask = 0; break; } // Special case handled in the shaders for empty write mask to completely skip // a disabled render target: all keep bits are set. if (!write_mask) { keep_mask_low = keep_mask_high = ~uint32_t(0); } } void DxbcShaderTranslator::StartPixelShader_LoadROVParameters() { bool color_targets_written = writes_any_color_target(); // *************************************************************************** // Get EDRAM offsets for the pixel: // system_temp_rov_params_.y - for depth (absolute). // system_temp_rov_params_.z - for 32bpp color (base-relative). // system_temp_rov_params_.w - for 64bpp color (base-relative). // *************************************************************************** // Extract the resolution scale as log2(scale)/2 specific for 1 (-> 0) and // 4 (-> 1) to a temp SGPR. uint32_t resolution_scale_log2_temp = PushSystemTemp(); system_constants_used_ |= 1ull << kSysConst_EdramResolutionSquareScale_Index; DxbcOpUShR(DxbcDest::R(resolution_scale_log2_temp, 0b0001), DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_EdramResolutionSquareScale_Vec) .Select(kSysConst_EdramResolutionSquareScale_Comp), DxbcSrc::LU(2)); // Convert the pixel position (if resolution scale is 4, this will be 2x2 // bigger) to integer to system_temp_rov_params_.zw. // system_temp_rov_params_.z = X host pixel position as uint // system_temp_rov_params_.w = Y host pixel position as uint in_position_xy_used_ = true; DxbcOpFToU(DxbcDest::R(system_temp_rov_params_, 0b1100), DxbcSrc::V(uint32_t(InOutRegister::kPSInPosition), 0b01000000)); // Revert the resolution scale to convert the position to guest pixels. // system_temp_rov_params_.z = X guest pixel position / sample width // system_temp_rov_params_.w = Y guest pixel position / sample height DxbcOpUShR(DxbcDest::R(system_temp_rov_params_, 0b1100), DxbcSrc::R(system_temp_rov_params_), DxbcSrc::R(resolution_scale_log2_temp, DxbcSrc::kXXXX)); // Convert the position from pixels to samples. // system_temp_rov_params_.z = X guest sample 0 position // system_temp_rov_params_.w = Y guest sample 0 position system_constants_used_ |= 1ull << kSysConst_SampleCountLog2_Index; DxbcOpIShL(DxbcDest::R(system_temp_rov_params_, 0b1100), DxbcSrc::R(system_temp_rov_params_), DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_SampleCountLog2_Vec, (kSysConst_SampleCountLog2_Comp << 4) | ((kSysConst_SampleCountLog2_Comp + 1) << 6))); // Get 80x16 samples tile index - start dividing X by 80 by getting the high // part of the result of multiplication of X by 0xCCCCCCCD into X. // system_temp_rov_params_.x = (X * 0xCCCCCCCD) >> 32, or X / 80 * 64 // system_temp_rov_params_.z = X guest sample 0 position // system_temp_rov_params_.w = Y guest sample 0 position DxbcOpUMul(DxbcDest::R(system_temp_rov_params_, 0b0001), DxbcDest::Null(), DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kZZZZ), DxbcSrc::LU(0xCCCCCCCDu)); // Get 80x16 samples tile index - finish dividing X by 80 and divide Y by 16 // into system_temp_rov_params_.xy. // system_temp_rov_params_.x = X tile position // system_temp_rov_params_.y = Y tile position // system_temp_rov_params_.z = X guest sample 0 position // system_temp_rov_params_.w = Y guest sample 0 position DxbcOpUShR(DxbcDest::R(system_temp_rov_params_, 0b0011), DxbcSrc::R(system_temp_rov_params_, 0b00001100), DxbcSrc::LU(6, 4, 0, 0)); // Get the tile index to system_temp_rov_params_.y. // system_temp_rov_params_.x = X tile position // system_temp_rov_params_.y = tile index // system_temp_rov_params_.z = X guest sample 0 position // system_temp_rov_params_.w = Y guest sample 0 position system_constants_used_ |= 1ull << kSysConst_EdramPitchTiles_Index; DxbcOpUMAd(DxbcDest::R(system_temp_rov_params_, 0b0010), DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kYYYY), DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_EdramPitchTiles_Vec) .Select(kSysConst_EdramPitchTiles_Comp), DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kXXXX)); // Convert the tile index into a tile offset. // system_temp_rov_params_.x = X tile position // system_temp_rov_params_.y = tile offset // system_temp_rov_params_.z = X guest sample 0 position // system_temp_rov_params_.w = Y guest sample 0 position DxbcOpUMul(DxbcDest::Null(), DxbcDest::R(system_temp_rov_params_, 0b0010), DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kYYYY), DxbcSrc::LU(1280)); // Get tile-local X sample index into system_temp_rov_params_.z. // system_temp_rov_params_.y = tile offset // system_temp_rov_params_.z = X sample 0 position within the tile // system_temp_rov_params_.w = Y guest sample 0 position DxbcOpIMAd(DxbcDest::R(system_temp_rov_params_, 0b0100), DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kXXXX), DxbcSrc::LI(-80), DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kZZZZ)); // Get tile-local Y sample index into system_temp_rov_params_.w. // system_temp_rov_params_.y = tile offset // system_temp_rov_params_.z = X sample 0 position within the tile // system_temp_rov_params_.w = Y sample 0 position within the tile DxbcOpAnd(DxbcDest::R(system_temp_rov_params_, 0b1000), DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kWWWW), DxbcSrc::LU(15)); // Go to the target row within the tile in system_temp_rov_params_.y. // system_temp_rov_params_.y = row offset // system_temp_rov_params_.z = X sample 0 position within the tile DxbcOpIMAd(DxbcDest::R(system_temp_rov_params_, 0b0010), DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kWWWW), DxbcSrc::LI(80), DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kYYYY)); // Choose in which 40-sample half of the tile the pixel is, for swapping // 40-sample columns when accessing the depth buffer - games expect this // behavior when writing depth back to the EDRAM via color writing (GTA IV, // Halo 3). // system_temp_rov_params_.x = tile-local sample 0 X >= 40 // system_temp_rov_params_.y = row offset // system_temp_rov_params_.z = X sample 0 position within the tile DxbcOpUGE(DxbcDest::R(system_temp_rov_params_, 0b0001), DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kZZZZ), DxbcSrc::LU(40)); // Choose what to add to the depth/stencil X position. // system_temp_rov_params_.x = 40 or -40 offset for the depth buffer // system_temp_rov_params_.y = row offset // system_temp_rov_params_.z = X sample 0 position within the tile DxbcOpMovC(DxbcDest::R(system_temp_rov_params_, 0b0001), DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kXXXX), DxbcSrc::LI(-40), DxbcSrc::LI(40)); // Flip tile halves for the depth/stencil buffer. // system_temp_rov_params_.x = X sample 0 position within the depth tile // system_temp_rov_params_.y = row offset // system_temp_rov_params_.z = X sample 0 position within the tile DxbcOpIAdd(DxbcDest::R(system_temp_rov_params_, 0b0001), DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kZZZZ), DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kXXXX)); if (color_targets_written) { // Write 32bpp color offset to system_temp_rov_params_.z. // system_temp_rov_params_.x = X sample 0 position within the depth tile // system_temp_rov_params_.y = row offset // system_temp_rov_params_.z = unscaled 32bpp color offset DxbcOpIAdd(DxbcDest::R(system_temp_rov_params_, 0b0100), DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kYYYY), DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kZZZZ)); } // Write depth/stencil offset to system_temp_rov_params_.y. // system_temp_rov_params_.y = unscaled 32bpp depth/stencil offset // system_temp_rov_params_.z = unscaled 32bpp color offset if needed DxbcOpIAdd(DxbcDest::R(system_temp_rov_params_, 0b0010), DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kYYYY), DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kXXXX)); // Add the EDRAM base for depth/stencil. // system_temp_rov_params_.y = unscaled 32bpp depth/stencil address // system_temp_rov_params_.z = unscaled 32bpp color offset if needed system_constants_used_ |= 1ull << kSysConst_EdramDepthBaseDwords_Index; DxbcOpIAdd(DxbcDest::R(system_temp_rov_params_, 0b0010), DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kYYYY), DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_EdramDepthBaseDwords_Vec) .Select(kSysConst_EdramDepthBaseDwords_Comp)); // Apply the resolution scale. DxbcOpIf(true, DxbcSrc::R(resolution_scale_log2_temp, DxbcSrc::kXXXX)); // Release resolution_scale_log2_temp. PopSystemTemp(); { DxbcDest offsets_dest(DxbcDest::R(system_temp_rov_params_, color_targets_written ? 0b0110 : 0b0010)); // Scale the offsets by the resolution scale. // system_temp_rov_params_.y = scaled 32bpp depth/stencil first host pixel // address // system_temp_rov_params_.z = scaled 32bpp color first host pixel offset if // needed DxbcOpIShL(offsets_dest, DxbcSrc::R(system_temp_rov_params_), DxbcSrc::LU(2)); // Add host pixel offsets. // system_temp_rov_params_.y = scaled 32bpp depth/stencil address // system_temp_rov_params_.z = scaled 32bpp color offset if needed in_position_xy_used_ = true; for (uint32_t i = 0; i < 2; ++i) { // Convert a position component to integer. DxbcOpFToU(DxbcDest::R(system_temp_rov_params_, 0b0001), DxbcSrc::V(uint32_t(InOutRegister::kPSInPosition)).Select(i)); // Insert the host pixel offset on each axis. DxbcOpBFI(offsets_dest, DxbcSrc::LU(1), DxbcSrc::LU(i), DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kXXXX), DxbcSrc::R(system_temp_rov_params_)); } } // Close the resolution scale conditional. DxbcOpEndIf(); if (color_targets_written) { // Get the 64bpp color offset to system_temp_rov_params_.w. // TODO(Triang3l): Find some game that aliases 64bpp with 32bpp to emulate // the real layout. // system_temp_rov_params_.y = scaled 32bpp depth/stencil address // system_temp_rov_params_.z = scaled 32bpp color offset // system_temp_rov_params_.w = scaled 64bpp color offset DxbcOpIShL(DxbcDest::R(system_temp_rov_params_, 0b1000), DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kZZZZ), DxbcSrc::LU(1)); } // *************************************************************************** // Sample coverage to system_temp_rov_params_.x. // *************************************************************************** // Using ForcedSampleCount of 4 (2 is not supported on Nvidia), so for 2x // MSAA, handling samples 0 and 3 (upper-left and lower-right) as 0 and 1. // Check if 4x MSAA is enabled. system_constants_used_ |= 1ull << kSysConst_SampleCountLog2_Index; DxbcOpIf(true, DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_SampleCountLog2_Vec) .Select(kSysConst_SampleCountLog2_Comp)); { // Copy the 4x AA coverage to system_temp_rov_params_.x, making top-right // the sample [2] and bottom-left the sample [1] (the opposite of Direct3D // 12), because on the Xbox 360, 2x MSAA doubles the storage width, 4x MSAA // doubles the storage height. // Flip samples in bits 0:1 to bits 29:30. DxbcOpBFRev(DxbcDest::R(system_temp_rov_params_, 0b0001), DxbcSrc::VCoverage()); DxbcOpUShR(DxbcDest::R(system_temp_rov_params_, 0b0001), DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kXXXX), DxbcSrc::LU(29)); DxbcOpBFI(DxbcDest::R(system_temp_rov_params_, 0b0001), DxbcSrc::LU(2), DxbcSrc::LU(1), DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kXXXX), DxbcSrc::VCoverage()); } // Handle 1 or 2 samples. DxbcOpElse(); { // Extract sample 3 coverage, which will be used as sample 1. DxbcOpUBFE(DxbcDest::R(system_temp_rov_params_, 0b0001), DxbcSrc::LU(1), DxbcSrc::LU(3), DxbcSrc::VCoverage()); // Combine coverage of samples 0 (in bit 0 of vCoverage) and 3 (in bit 0 of // system_temp_rov_params_.x). DxbcOpBFI(DxbcDest::R(system_temp_rov_params_, 0b0001), DxbcSrc::LU(31), DxbcSrc::LU(1), DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kXXXX), DxbcSrc::VCoverage()); } // Close the 4x MSAA conditional. DxbcOpEndIf(); } void DxbcShaderTranslator::ROV_DepthStencilTest() { bool depth_stencil_early = ROV_IsDepthStencilEarly(); uint32_t temp = PushSystemTemp(); DxbcDest temp_x_dest(DxbcDest::R(temp, 0b0001)); DxbcSrc temp_x_src(DxbcSrc::R(temp, DxbcSrc::kXXXX)); DxbcDest temp_y_dest(DxbcDest::R(temp, 0b0010)); DxbcSrc temp_y_src(DxbcSrc::R(temp, DxbcSrc::kYYYY)); DxbcDest temp_z_dest(DxbcDest::R(temp, 0b0100)); DxbcSrc temp_z_src(DxbcSrc::R(temp, DxbcSrc::kZZZZ)); DxbcDest temp_w_dest(DxbcDest::R(temp, 0b1000)); DxbcSrc temp_w_src(DxbcSrc::R(temp, DxbcSrc::kWWWW)); // Check whether depth/stencil is enabled. // temp.x = kSysFlag_ROVDepthStencil system_constants_used_ |= 1ull << kSysConst_Flags_Index; DxbcOpAnd(temp_x_dest, DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_Flags_Vec) .Select(kSysConst_Flags_Comp), DxbcSrc::LU(kSysFlag_ROVDepthStencil)); // Open the depth/stencil enabled conditional. // temp.x = free DxbcOpIf(true, temp_x_src); for (uint32_t i = 0; i < 4; ++i) { // With early depth/stencil, depth/stencil writing may be deferred to the // end of the shader to prevent writing in case something (like alpha test, // which is dynamic GPU state) discards the pixel. So, write directly to the // persistent register, system_temp_rov_depth_stencil_, instead of a local // temporary register. DxbcDest sample_depth_stencil_dest( depth_stencil_early ? DxbcDest::R(system_temp_rov_depth_stencil_, 1 << i) : temp_x_dest); DxbcSrc sample_depth_stencil_src( depth_stencil_early ? DxbcSrc::R(system_temp_rov_depth_stencil_).Select(i) : temp_x_src); if (!i) { if (writes_depth()) { // Convert the shader-generated depth to 24-bit, using temp.x as // temporary. ROV_DepthTo24Bit(system_temp_rov_depth_stencil_, 0, system_temp_rov_depth_stencil_, 0, temp, 0); } else { // Load the first sample's Z*W and W to temp.xy - need this regardless // of coverage for polygon offset. // temp.x = first sample's clip space Z*W // temp.y = first sample's clip space W DxbcOpEvalSampleIndex( DxbcDest::R(temp, 0b0011), DxbcSrc::V(uint32_t(InOutRegister::kPSInClipSpaceZW)), DxbcSrc::LU(0)); // Calculate the first sample's Z/W to temp.x for conversion to 24-bit // and depth test. // temp.x? = first sample's clip space Z // temp.y = free DxbcOpDiv(sample_depth_stencil_dest, temp_x_src, temp_y_src, true); // Apply viewport Z range to the first sample because this would affect // the slope-scaled depth bias (tested on PC on Direct3D 12, by // comparing the fraction of the polygon's area with depth clamped - // affected by the constant bias, but not affected by the slope-scaled // bias, also depth range clamping should be done after applying the // offset as well). // temp.x? = first sample's viewport space Z system_constants_used_ |= 1ull << kSysConst_EdramDepthRange_Index; DxbcOpMAd(sample_depth_stencil_dest, sample_depth_stencil_src, DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_EdramDepthRange_Vec) .Select(kSysConst_EdramDepthRangeScale_Comp), DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_EdramDepthRange_Vec) .Select(kSysConst_EdramDepthRangeOffset_Comp), true); // Get the derivatives of a sample's depth, for the slope-scaled polygon // offset. Probably not very significant that it's for the sample 0 // rather than for the center, likely neither is accurate because Xenos // probably calculates the slope between 16ths of a pixel according to // the meaning of the slope-scaled polygon offset in R5xx Acceleration. // temp.x? = first sample's viewport space Z // temp.y = ddx(z) // temp.z = ddy(z) DxbcOpDerivRTXCoarse(temp_y_dest, sample_depth_stencil_src); DxbcOpDerivRTYCoarse(temp_z_dest, sample_depth_stencil_src); // Get the maximum depth slope for polygon offset to temp.y. // https://docs.microsoft.com/en-us/windows/desktop/direct3d9/depth-bias // temp.x? = first sample's viewport space Z // temp.y = max(|ddx(z)|, |ddy(z)|) // temp.z = free DxbcOpMax(temp_y_dest, temp_y_src.Abs(), temp_z_src.Abs()); // Copy the needed polygon offset values to temp.zw. // temp.x? = first sample's viewport space Z // temp.y = max(|ddx(z)|, |ddy(z)|) // temp.z = polygon offset scale // temp.w = polygon offset bias in_front_face_used_ = true; system_constants_used_ |= (1ull << kSysConst_EdramPolyOffsetFront_Index) | (1ull << kSysConst_EdramPolyOffsetBack_Index); DxbcOpMovC( DxbcDest::R(temp, 0b1100), DxbcSrc::V(uint32_t(InOutRegister::kPSInFrontFace), DxbcSrc::kXXXX), DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_EdramPolyOffsetFront_Vec, (kSysConst_EdramPolyOffsetFrontScale_Comp << 4) | (kSysConst_EdramPolyOffsetFrontOffset_Comp << 6)), DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_EdramPolyOffsetBack_Vec, (kSysConst_EdramPolyOffsetBackScale_Comp << 4) | (kSysConst_EdramPolyOffsetBackOffset_Comp << 6))); // Apply the slope scale and the constant bias to the offset. // temp.x? = first sample's viewport space Z // temp.y = polygon offset // temp.z = free // temp.w = free DxbcOpMAd(temp_y_dest, temp_y_src, temp_z_src, temp_w_src); // Calculate the upper Z range bound to temp.z for clamping after // biasing. // temp.x? = first sample's viewport space Z // temp.y = polygon offset // temp.z = viewport maximum depth system_constants_used_ |= 1ull << kSysConst_EdramDepthRange_Index; DxbcOpAdd(temp_z_dest, DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_EdramDepthRange_Vec) .Select(kSysConst_EdramDepthRangeOffset_Comp), DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_EdramDepthRange_Vec) .Select(kSysConst_EdramDepthRangeScale_Comp)); } } // Get if the current sample is covered to temp.w. // temp.x = first sample's viewport space Z or 24-bit oDepth // temp.y = polygon offset if not writing to oDepth // temp.z = viewport maximum depth if not writing to oDepth // temp.w = coverage of the current sample DxbcOpAnd(temp_w_dest, DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kXXXX), DxbcSrc::LU(1 << i)); // Check if the current sample is covered. Release 1 VGPR. // temp.x = first sample's viewport space Z or 24-bit oDepth // temp.y = polygon offset if not writing to oDepth // temp.z = viewport maximum depth if not writing to oDepth // temp.w = free DxbcOpIf(true, temp_w_src); if (writes_depth()) { // Copy the 24-bit depth common to all samples to sample_depth_stencil. // temp.x = shader-generated 24-bit depth DxbcOpMov(sample_depth_stencil_dest, DxbcSrc::R(system_temp_rov_depth_stencil_, DxbcSrc::kXXXX)); } else { if (i) { // Sample's depth precalculated for sample 0 (for slope-scaled depth // bias calculation), but need to calculate it for other samples. // // Reusing temp.x because it may contain the depth value for the first // sample, but it has been written already. // // For 2x: // Using ForcedSampleCount of 4 (2 is not supported on Nvidia), so for // 2x MSAA, handling samples 0 and 3 (upper-left and lower-right) as 0 // and 1. Thus, evaluating Z/W at sample 3 when 4x is not enabled. // // For 4x: // Direct3D 12's sample pattern has 1 as top-right, 2 as bottom-left. // Xbox 360's render targets are 2x taller with 2x MSAA, 2x wider with // 4x, thus, likely 1 is bottom-left, 2 is top-right - swapping these. // // temp.x = sample's clip space Z*W // temp.y = polygon offset if not writing to oDepth // temp.z = viewport maximum depth if not writing to oDepth // temp.w = sample's clip space W if (i == 1) { system_constants_used_ |= 1ull << kSysConst_SampleCountLog2_Index; DxbcOpMovC(sample_depth_stencil_dest, DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_SampleCountLog2_Vec) .Select(kSysConst_SampleCountLog2_Comp), DxbcSrc::LU(3), DxbcSrc::LU(2)); DxbcOpEvalSampleIndex( DxbcDest::R(temp, 0b1001), DxbcSrc::V(uint32_t(InOutRegister::kPSInClipSpaceZW), 0b01000000), sample_depth_stencil_src); } else { DxbcOpEvalSampleIndex( DxbcDest::R(temp, 0b1001), DxbcSrc::V(uint32_t(InOutRegister::kPSInClipSpaceZW), 0b01000000), DxbcSrc::LU(i == 2 ? 1 : i)); } // Calculate Z/W for the current sample from the evaluated Z*W and W. // temp.x? = sample's clip space Z // temp.y = polygon offset if not writing to oDepth // temp.z = viewport maximum depth if not writing to oDepth // temp.w = free DxbcOpDiv(sample_depth_stencil_dest, temp_x_src, temp_w_src, true); // Apply viewport Z range the same way as it was applied to sample 0. // temp.x? = sample's viewport space Z // temp.y = polygon offset if not writing to oDepth // temp.z = viewport maximum depth if not writing to oDepth system_constants_used_ |= 1ull << kSysConst_EdramDepthRange_Index; DxbcOpMAd(sample_depth_stencil_dest, sample_depth_stencil_src, DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_EdramDepthRange_Vec) .Select(kSysConst_EdramDepthRangeScale_Comp), DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_EdramDepthRange_Vec) .Select(kSysConst_EdramDepthRangeOffset_Comp), true); } // Add the bias to the depth of the sample. // temp.x? = sample's unclamped biased Z // temp.y = polygon offset if not writing to oDepth // temp.z = viewport maximum depth if not writing to oDepth DxbcOpAdd(sample_depth_stencil_dest, sample_depth_stencil_src, temp_y_src); // Clamp the biased depth to the lower viewport depth bound. // temp.x? = sample's lower-clamped biased Z // temp.y = polygon offset if not writing to oDepth // temp.z = viewport maximum depth if not writing to oDepth system_constants_used_ |= 1ull << kSysConst_EdramDepthRange_Index; DxbcOpMax(sample_depth_stencil_dest, sample_depth_stencil_src, DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_EdramDepthRange_Vec) .Select(kSysConst_EdramDepthRangeOffset_Comp)); // Clamp the biased depth to the upper viewport depth bound. // temp.x? = sample's biased Z // temp.y = polygon offset if not writing to oDepth // temp.z = viewport maximum depth if not writing to oDepth DxbcOpMin(sample_depth_stencil_dest, sample_depth_stencil_src, temp_z_src, true); // Convert the sample's depth to 24-bit, using temp.w as a temporary. // temp.x? = sample's 24-bit Z // temp.y = polygon offset if not writing to oDepth // temp.z = viewport maximum depth if not writing to oDepth ROV_DepthTo24Bit(sample_depth_stencil_src.index_1d_.index_, sample_depth_stencil_src.swizzle_ & 3, sample_depth_stencil_src.index_1d_.index_, sample_depth_stencil_src.swizzle_ & 3, temp, 3); } // Load the old depth/stencil value to temp.w. // temp.x? = sample's 24-bit Z // temp.y = polygon offset if not writing to oDepth // temp.z = viewport maximum depth if not writing to oDepth // temp.w = old depth/stencil if (uav_index_edram_ == kBindingIndexUnallocated) { uav_index_edram_ = uav_count_++; } DxbcOpLdUAVTyped(temp_w_dest, DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kYYYY), 1, DxbcSrc::U(uav_index_edram_, uint32_t(UAVRegister::kEdram), DxbcSrc::kXXXX)); uint32_t sample_temp = PushSystemTemp(); DxbcDest sample_temp_x_dest(DxbcDest::R(sample_temp, 0b0001)); DxbcSrc sample_temp_x_src(DxbcSrc::R(sample_temp, DxbcSrc::kXXXX)); DxbcDest sample_temp_y_dest(DxbcDest::R(sample_temp, 0b0010)); DxbcSrc sample_temp_y_src(DxbcSrc::R(sample_temp, DxbcSrc::kYYYY)); DxbcDest sample_temp_z_dest(DxbcDest::R(sample_temp, 0b0100)); DxbcSrc sample_temp_z_src(DxbcSrc::R(sample_temp, DxbcSrc::kZZZZ)); // Depth test. // Extract the old depth part to sample_depth_stencil. // sample_temp.x = old depth DxbcOpUShR(sample_temp_x_dest, temp_w_src, DxbcSrc::LU(8)); // Get the difference between the new and the old depth, > 0 - greater, // == 0 - equal, < 0 - less. // sample_temp.x = old depth // sample_temp.y = depth difference DxbcOpIAdd(sample_temp_y_dest, sample_depth_stencil_src, -sample_temp_x_src); // Check if the depth is "less" or "greater or equal". // sample_temp.x = old depth // sample_temp.y = depth difference // sample_temp.z = depth difference less than 0 DxbcOpILT(sample_temp_z_dest, sample_temp_y_src, DxbcSrc::LI(0)); // Choose the passed depth function bits for "less" or for "greater". // sample_temp.x = old depth // sample_temp.y = depth difference // sample_temp.z = depth function passed bits for "less" or "greater" DxbcOpMovC(sample_temp_z_dest, sample_temp_z_src, DxbcSrc::LU(kSysFlag_ROVDepthPassIfLess), DxbcSrc::LU(kSysFlag_ROVDepthPassIfGreater)); // Do the "equal" testing. // sample_temp.x = old depth // sample_temp.y = depth function passed bits // sample_temp.z = free DxbcOpMovC(sample_temp_y_dest, sample_temp_y_src, sample_temp_z_src, DxbcSrc::LU(kSysFlag_ROVDepthPassIfEqual)); // Mask the resulting bits with the ones that should pass. // sample_temp.x = old depth // sample_temp.y = masked depth function passed bits // sample_temp.z = free system_constants_used_ |= 1ull << kSysConst_Flags_Index; DxbcOpAnd(sample_temp_y_dest, sample_temp_y_src, DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_Flags_Vec) .Select(kSysConst_Flags_Comp)); // Check if depth test has passed. // sample_temp.x = old depth // sample_temp.y = free DxbcOpIf(true, sample_temp_y_src); { // Extract the depth write flag. // sample_temp.x = old depth // sample_temp.y = depth write mask system_constants_used_ |= 1ull << kSysConst_Flags_Index; DxbcOpAnd(sample_temp_y_dest, DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_Flags_Vec) .Select(kSysConst_Flags_Comp), DxbcSrc::LU(kSysFlag_ROVDepthWrite)); // If depth writing is disabled, don't change the depth. // temp.x? = resulting sample depth after the depth test // temp.y = polygon offset if not writing to oDepth // temp.z = viewport maximum depth if not writing to oDepth // temp.w = old depth/stencil // sample_temp.x = free // sample_temp.y = free DxbcOpMovC(sample_depth_stencil_dest, sample_temp_y_src, sample_depth_stencil_src, sample_temp_x_src); } // Depth test has failed. DxbcOpElse(); { // Exclude the bit from the covered sample mask. // sample_temp.x = old depth DxbcOpAnd(DxbcDest::R(system_temp_rov_params_, 0b0001), DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kXXXX), DxbcSrc::LU(~uint32_t(1 << i))); } DxbcOpEndIf(); // Create packed depth/stencil, with the stencil value unchanged at this // point. // temp.x? = resulting sample depth, current resulting stencil // temp.y = polygon offset if not writing to oDepth // temp.z = viewport maximum depth if not writing to oDepth // temp.w = old depth/stencil DxbcOpBFI(sample_depth_stencil_dest, DxbcSrc::LU(24), DxbcSrc::LU(8), sample_depth_stencil_src, temp_w_src); // Stencil test. // Extract the stencil test bit. // sample_temp.x = stencil test enabled system_constants_used_ |= 1ull << kSysConst_Flags_Index; DxbcOpAnd(sample_temp_x_dest, DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_Flags_Vec) .Select(kSysConst_Flags_Comp), DxbcSrc::LU(kSysFlag_ROVStencilTest)); // Check if stencil test is enabled. // sample_temp.x = free DxbcOpIf(true, sample_temp_x_src); { DxbcSrc stencil_front_src( DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_EdramStencil_Front_Vec)); DxbcSrc stencil_back_src( DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_EdramStencil_Back_Vec)); // Check the current face to get the reference and apply the read mask. in_front_face_used_ = true; DxbcOpIf(true, DxbcSrc::V(uint32_t(InOutRegister::kPSInFrontFace), DxbcSrc::kXXXX)); system_constants_used_ |= 1ull << kSysConst_EdramStencil_Index; for (uint32_t j = 0; j < 2; ++j) { if (j) { // Go to the back face. DxbcOpElse(); } DxbcSrc stencil_side_src(j ? stencil_back_src : stencil_front_src); // Read-mask the stencil reference. // sample_temp.x = read-masked stencil reference DxbcOpAnd( sample_temp_x_dest, stencil_side_src.Select(kSysConst_EdramStencil_Reference_Comp), stencil_side_src.Select(kSysConst_EdramStencil_ReadMask_Comp)); // Read-mask the old stencil value (also dropping the depth bits). // sample_temp.x = read-masked stencil reference // sample_temp.y = read-masked old stencil DxbcOpAnd( sample_temp_y_dest, temp_w_src, stencil_side_src.Select(kSysConst_EdramStencil_ReadMask_Comp)); } // Close the face check. DxbcOpEndIf(); // Get the difference between the stencil reference and the old stencil, // > 0 - greater, == 0 - equal, < 0 - less. // sample_temp.x = stencil difference // sample_temp.y = free DxbcOpIAdd(sample_temp_x_dest, sample_temp_x_src, -sample_temp_y_src); // Check if the stencil is "less" or "greater or equal". // sample_temp.x = stencil difference // sample_temp.y = stencil difference less than 0 DxbcOpILT(sample_temp_y_dest, sample_temp_x_src, DxbcSrc::LI(0)); // Choose the passed depth function bits for "less" or for "greater". // sample_temp.x = stencil difference // sample_temp.y = stencil function passed bits for "less" or "greater" DxbcOpMovC(sample_temp_y_dest, sample_temp_y_src, DxbcSrc::LU(uint32_t(xenos::CompareFunction::kLess)), DxbcSrc::LU(uint32_t(xenos::CompareFunction::kGreater))); // Do the "equal" testing. // sample_temp.x = stencil function passed bits // sample_temp.y = free DxbcOpMovC(sample_temp_x_dest, sample_temp_x_src, sample_temp_y_src, DxbcSrc::LU(uint32_t(xenos::CompareFunction::kEqual))); // Get the comparison function and the operations for the current face. // sample_temp.x = stencil function passed bits // sample_temp.y = stencil function and operations in_front_face_used_ = true; system_constants_used_ |= 1ull << kSysConst_EdramStencil_Index; DxbcOpMovC( sample_temp_y_dest, DxbcSrc::V(uint32_t(InOutRegister::kPSInFrontFace), DxbcSrc::kXXXX), stencil_front_src.Select(kSysConst_EdramStencil_FuncOps_Comp), stencil_back_src.Select(kSysConst_EdramStencil_FuncOps_Comp)); // Mask the resulting bits with the ones that should pass (the comparison // function is in the low 3 bits of the constant, and only ANDing 3-bit // values with it, so safe not to UBFE the function). // sample_temp.x = stencil test result // sample_temp.y = stencil function and operations DxbcOpAnd(sample_temp_x_dest, sample_temp_x_src, sample_temp_y_src); // Handle passing and failure of the stencil test, to choose the operation // and to discard the sample. // sample_temp.x = free // sample_temp.y = stencil function and operations DxbcOpIf(true, sample_temp_x_src); { // Check if depth test has passed for this sample to sample_temp.y (the // sample will only be processed if it's covered, so the only thing that // could unset the bit at this point that matters is the depth test). // sample_temp.x = depth test result // sample_temp.y = stencil function and operations DxbcOpAnd(sample_temp_x_dest, DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kXXXX), DxbcSrc::LU(1 << i)); // Choose the bit offset of the stencil operation. // sample_temp.x = sample operation offset // sample_temp.y = stencil function and operations DxbcOpMovC(sample_temp_x_dest, sample_temp_x_src, DxbcSrc::LU(6), DxbcSrc::LU(9)); // Extract the stencil operation. // sample_temp.x = stencil operation // sample_temp.y = free DxbcOpUBFE(sample_temp_x_dest, DxbcSrc::LU(3), sample_temp_x_src, sample_temp_y_src); } // Stencil test has failed. DxbcOpElse(); { // Extract the stencil fail operation. // sample_temp.x = stencil operation // sample_temp.y = free DxbcOpUBFE(sample_temp_x_dest, DxbcSrc::LU(3), DxbcSrc::LU(3), sample_temp_y_src); // Exclude the bit from the covered sample mask. // sample_temp.x = stencil operation DxbcOpAnd(DxbcDest::R(system_temp_rov_params_, 0b0001), DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kXXXX), DxbcSrc::LU(~uint32_t(1 << i))); } // Close the stencil pass check. DxbcOpEndIf(); // Open the stencil operation switch for writing the new stencil (not // caring about bits 8:31). // sample_temp.x = will contain unmasked new stencil in 0:7 and junk above DxbcOpSwitch(sample_temp_x_src); { // Zero. DxbcOpCase(DxbcSrc::LU(uint32_t(xenos::StencilOp::kZero))); DxbcOpMov(sample_temp_x_dest, DxbcSrc::LU(0)); DxbcOpBreak(); // Replace. DxbcOpCase(DxbcSrc::LU(uint32_t(xenos::StencilOp::kReplace))); in_front_face_used_ = true; system_constants_used_ |= 1ull << kSysConst_EdramStencil_Index; DxbcOpMovC( sample_temp_x_dest, DxbcSrc::V(uint32_t(InOutRegister::kPSInFrontFace), DxbcSrc::kXXXX), stencil_front_src.Select(kSysConst_EdramStencil_Reference_Comp), stencil_back_src.Select(kSysConst_EdramStencil_Reference_Comp)); DxbcOpBreak(); // Increment and clamp. DxbcOpCase(DxbcSrc::LU(uint32_t(xenos::StencilOp::kIncrementClamp))); { // Clear the upper bits for saturation. DxbcOpAnd(sample_temp_x_dest, temp_w_src, DxbcSrc::LU(UINT8_MAX)); // Increment. DxbcOpIAdd(sample_temp_x_dest, sample_temp_x_src, DxbcSrc::LI(1)); // Clamp. DxbcOpIMin(sample_temp_x_dest, sample_temp_x_src, DxbcSrc::LI(UINT8_MAX)); } DxbcOpBreak(); // Decrement and clamp. DxbcOpCase(DxbcSrc::LU(uint32_t(xenos::StencilOp::kDecrementClamp))); { // Clear the upper bits for saturation. DxbcOpAnd(sample_temp_x_dest, temp_w_src, DxbcSrc::LU(UINT8_MAX)); // Increment. DxbcOpIAdd(sample_temp_x_dest, sample_temp_x_src, DxbcSrc::LI(-1)); // Clamp. DxbcOpIMax(sample_temp_x_dest, sample_temp_x_src, DxbcSrc::LI(0)); } DxbcOpBreak(); // Invert. DxbcOpCase(DxbcSrc::LU(uint32_t(xenos::StencilOp::kInvert))); DxbcOpNot(sample_temp_x_dest, temp_w_src); DxbcOpBreak(); // Increment and wrap. DxbcOpCase(DxbcSrc::LU(uint32_t(xenos::StencilOp::kIncrementWrap))); DxbcOpIAdd(sample_temp_x_dest, temp_w_src, DxbcSrc::LI(1)); DxbcOpBreak(); // Decrement and wrap. DxbcOpCase(DxbcSrc::LU(uint32_t(xenos::StencilOp::kDecrementWrap))); DxbcOpIAdd(sample_temp_x_dest, temp_w_src, DxbcSrc::LI(-1)); DxbcOpBreak(); // Keep. DxbcOpDefault(); DxbcOpMov(sample_temp_x_dest, temp_w_src); DxbcOpBreak(); } // Close the new stencil switch. DxbcOpEndSwitch(); // Select the stencil write mask for the face. // sample_temp.x = unmasked new stencil in 0:7 and junk above // sample_temp.y = stencil write mask in_front_face_used_ = true; system_constants_used_ |= 1ull << kSysConst_EdramStencil_Index; DxbcOpMovC( sample_temp_y_dest, DxbcSrc::V(uint32_t(InOutRegister::kPSInFrontFace), DxbcSrc::kXXXX), stencil_front_src.Select(kSysConst_EdramStencil_WriteMask_Comp), stencil_back_src.Select(kSysConst_EdramStencil_WriteMask_Comp)); // Apply the write mask to the new stencil, also dropping the upper 24 // bits. // sample_temp.x = masked new stencil // sample_temp.y = stencil write mask DxbcOpAnd(sample_temp_x_dest, sample_temp_x_src, sample_temp_y_src); // Invert the write mask for keeping the old stencil and the depth bits. // sample_temp.x = masked new stencil // sample_temp.y = inverted stencil write mask DxbcOpNot(sample_temp_y_dest, sample_temp_y_src); // Remove the bits that will be replaced from the new combined // depth/stencil. // sample_temp.x = masked new stencil // sample_temp.y = free DxbcOpAnd(sample_depth_stencil_dest, sample_depth_stencil_src, sample_temp_y_src); // Merge the old and the new stencil. // temp.x? = resulting sample depth/stencil // temp.y = polygon offset if not writing to oDepth // temp.z = viewport maximum depth if not writing to oDepth // temp.w = old depth/stencil // sample_temp.x = free DxbcOpOr(sample_depth_stencil_dest, sample_depth_stencil_src, sample_temp_x_src); } // Close the stencil test check. DxbcOpEndIf(); // Check if the depth/stencil has failed not to modify the depth if it has. // sample_temp.x = whether depth/stencil has passed for this sample DxbcOpAnd(sample_temp_x_dest, DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kXXXX), DxbcSrc::LU(1 << i)); // If the depth/stencil test has failed, don't change the depth. // sample_temp.x = free DxbcOpIf(false, sample_temp_x_src); { // Copy the new stencil over the old depth. // temp.x? = resulting sample depth/stencil // temp.y = polygon offset if not writing to oDepth // temp.z = viewport maximum depth if not writing to oDepth // temp.w = old depth/stencil DxbcOpBFI(sample_depth_stencil_dest, DxbcSrc::LU(8), DxbcSrc::LU(0), sample_depth_stencil_src, temp_w_src); } // Close the depth/stencil passing check. DxbcOpEndIf(); // Check if the new depth/stencil is different, and thus needs to be // written, to temp.w. // temp.x? = resulting sample depth/stencil // temp.y = polygon offset if not writing to oDepth // temp.z = viewport maximum depth if not writing to oDepth // temp.w = whether depth/stencil has been modified DxbcOpINE(temp_w_dest, sample_depth_stencil_src, temp_w_src); // Check if need to write. // temp.x? = resulting sample depth/stencil // temp.y = polygon offset if not writing to oDepth // temp.z = viewport maximum depth if not writing to oDepth // temp.w = free DxbcOpIf(true, temp_w_src); { if (depth_stencil_early) { // Get if early depth/stencil write is enabled to temp.w. // temp.w = whether early depth/stencil write is enabled system_constants_used_ |= 1ull << kSysConst_Flags_Index; DxbcOpAnd(temp_w_dest, DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_Flags_Vec) .Select(kSysConst_Flags_Comp), DxbcSrc::LU(kSysFlag_ROVDepthStencilEarlyWrite)); // Check if need to write early. // temp.w = free DxbcOpIf(true, temp_w_src); } // Write the new depth/stencil. if (uav_index_edram_ == kBindingIndexUnallocated) { uav_index_edram_ = uav_count_++; } DxbcOpStoreUAVTyped( DxbcDest::U(uav_index_edram_, uint32_t(UAVRegister::kEdram)), DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kYYYY), 1, sample_depth_stencil_src); if (depth_stencil_early) { // Need to still run the shader to know whether to write the // depth/stencil value. DxbcOpElse(); // Set sample bit out of bits 4:7 of system_temp_rov_params_.x if need // to write later (after checking if the sample is not discarded by a // kill instruction, alphatest or alpha-to-coverage). DxbcOpOr(DxbcDest::R(system_temp_rov_params_, 0b0001), DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kXXXX), DxbcSrc::LU(1 << (4 + i))); // Close the early depth/stencil check. DxbcOpEndIf(); } } // Close the write check. DxbcOpEndIf(); // Release sample_temp. PopSystemTemp(); // Close the sample conditional. DxbcOpEndIf(); // Go to the next sample (samples are at +0, +80, +1, +81, so need to do // +80, -79, +80 and -81 after each sample). system_constants_used_ |= 1ull << kSysConst_EdramResolutionSquareScale_Index; DxbcOpIMAd(DxbcDest::R(system_temp_rov_params_, 0b0010), DxbcSrc::LI((i & 1) ? -78 - i : 80), DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_EdramResolutionSquareScale_Vec) .Select(kSysConst_EdramResolutionSquareScale_Comp), DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kYYYY)); } if (ROV_IsDepthStencilEarly()) { // Check if safe to discard the whole 2x2 quad early, without running the // translated pixel shader, by checking if coverage is 0 in all pixels in // the quad and if there are no samples which failed the depth test, but // where stencil was modified and needs to be written in the end. Must // reject at 2x2 quad granularity because texture fetches need derivatives. // temp.x = coverage | deferred depth/stencil write DxbcOpAnd(DxbcDest::R(temp, 0b0001), DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kXXXX), DxbcSrc::LU(0b11111111)); // temp.x = 1.0 if any sample is covered or potentially needs stencil write // in the end of the shader in the current pixel DxbcOpMovC(DxbcDest::R(temp, 0b0001), DxbcSrc::R(temp, DxbcSrc::kXXXX), DxbcSrc::LF(1.0f), DxbcSrc::LF(0.0f)); // temp.x = 1.0 if any sample is covered or potentially needs stencil write // in the end of the shader in the current pixel // temp.y = non-zero if anything is covered in the pixel across X DxbcOpDerivRTXFine(DxbcDest::R(temp, 0b0010), DxbcSrc::R(temp, DxbcSrc::kXXXX)); // temp.x = 1.0 if anything is covered in the current half of the quad // temp.y = free DxbcOpMovC(DxbcDest::R(temp, 0b0001), DxbcSrc::R(temp, DxbcSrc::kYYYY), DxbcSrc::LF(1.0f), DxbcSrc::R(temp, DxbcSrc::kXXXX)); // temp.x = 1.0 if anything is covered in the current half of the quad // temp.y = non-zero if anything is covered in the two pixels across Y DxbcOpDerivRTYCoarse(DxbcDest::R(temp, 0b0010), DxbcSrc::R(temp, DxbcSrc::kXXXX)); // temp.x = 1.0 if anything is covered in the current whole quad // temp.y = free DxbcOpMovC(DxbcDest::R(temp, 0b0001), DxbcSrc::R(temp, DxbcSrc::kYYYY), DxbcSrc::LF(1.0f), DxbcSrc::R(temp, DxbcSrc::kXXXX)); // End the shader if nothing is covered in the 2x2 quad after early // depth/stencil. // temp.x = free DxbcOpRetC(false, DxbcSrc::R(temp, DxbcSrc::kXXXX)); } // Close the large depth/stencil conditional. DxbcOpEndIf(); // Release temp. PopSystemTemp(); } void DxbcShaderTranslator::ROV_UnpackColor( uint32_t rt_index, uint32_t packed_temp, uint32_t packed_temp_components, uint32_t color_temp, uint32_t temp1, uint32_t temp1_component, uint32_t temp2, uint32_t temp2_component) { assert_true(color_temp != packed_temp || packed_temp_components == 0); DxbcSrc packed_temp_low( DxbcSrc::R(packed_temp).Select(packed_temp_components)); DxbcDest temp1_dest(DxbcDest::R(temp1, 1 << temp1_component)); DxbcSrc temp1_src(DxbcSrc::R(temp1).Select(temp1_component)); DxbcDest temp2_dest(DxbcDest::R(temp2, 1 << temp2_component)); DxbcSrc temp2_src(DxbcSrc::R(temp2).Select(temp2_component)); // Break register dependencies and initialize if there are not enough // components. The rest of the function will write at least RG (k_32_FLOAT and // k_32_32_FLOAT handled with the same default label), and if packed_temp is // the same as color_temp, the packed color won't be touched. DxbcOpMov(DxbcDest::R(color_temp, 0b1100), DxbcSrc::LF(0.0f, 0.0f, 0.0f, 1.0f)); // Choose the packing based on the render target's format. system_constants_used_ |= 1ull << kSysConst_EdramRTFormatFlags_Index; DxbcOpSwitch(DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_EdramRTFormatFlags_Vec) .Select(rt_index)); // *************************************************************************** // k_8_8_8_8 // k_8_8_8_8_GAMMA // *************************************************************************** for (uint32_t i = 0; i < 2; ++i) { DxbcOpCase(DxbcSrc::LU(ROV_AddColorFormatFlags( i ? xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA : xenos::ColorRenderTargetFormat::k_8_8_8_8))); // Unpack the components. DxbcOpUBFE(DxbcDest::R(color_temp), DxbcSrc::LU(8), DxbcSrc::LU(0, 8, 16, 24), packed_temp_low); // Convert from fixed-point. DxbcOpUToF(DxbcDest::R(color_temp), DxbcSrc::R(color_temp)); // Normalize. DxbcOpMul(DxbcDest::R(color_temp), DxbcSrc::R(color_temp), DxbcSrc::LF(1.0f / 255.0f)); if (i) { for (uint32_t j = 0; j < 3; ++j) { ConvertPWLGamma(false, color_temp, j, color_temp, j, temp1, temp1_component, temp2, temp2_component); } } DxbcOpBreak(); } // *************************************************************************** // k_2_10_10_10 // k_2_10_10_10_AS_10_10_10_10 // *************************************************************************** DxbcOpCase(DxbcSrc::LU( ROV_AddColorFormatFlags(xenos::ColorRenderTargetFormat::k_2_10_10_10))); DxbcOpCase(DxbcSrc::LU(ROV_AddColorFormatFlags( xenos::ColorRenderTargetFormat::k_2_10_10_10_AS_10_10_10_10))); { // Unpack the components. DxbcOpUBFE(DxbcDest::R(color_temp), DxbcSrc::LU(10, 10, 10, 2), DxbcSrc::LU(0, 10, 20, 30), packed_temp_low); // Convert from fixed-point. DxbcOpUToF(DxbcDest::R(color_temp), DxbcSrc::R(color_temp)); // Normalize. DxbcOpMul(DxbcDest::R(color_temp), DxbcSrc::R(color_temp), DxbcSrc::LF(1.0f / 1023.0f, 1.0f / 1023.0f, 1.0f / 1023.0f, 1.0f / 3.0f)); } DxbcOpBreak(); // *************************************************************************** // k_2_10_10_10_FLOAT // k_2_10_10_10_FLOAT_AS_16_16_16_16 // https://github.com/Microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexConvert.cpp // *************************************************************************** DxbcOpCase(DxbcSrc::LU(ROV_AddColorFormatFlags( xenos::ColorRenderTargetFormat::k_2_10_10_10_FLOAT))); DxbcOpCase(DxbcSrc::LU(ROV_AddColorFormatFlags( xenos::ColorRenderTargetFormat::k_2_10_10_10_FLOAT_AS_16_16_16_16))); { // Unpack the alpha. DxbcOpUBFE(DxbcDest::R(color_temp, 0b1000), DxbcSrc::LU(2), DxbcSrc::LU(30), packed_temp_low); // Convert the alpha from fixed-point. DxbcOpUToF(DxbcDest::R(color_temp, 0b1000), DxbcSrc::R(color_temp, DxbcSrc::kWWWW)); // Normalize the alpha. DxbcOpMul(DxbcDest::R(color_temp, 0b1000), DxbcSrc::R(color_temp, DxbcSrc::kWWWW), DxbcSrc::LF(1.0f / 3.0f)); // Process the components in reverse order because color_temp.r stores the // packed color which shouldn't be touched until G and B are converted if // packed_temp and color_temp are the same. for (int32_t i = 2; i >= 0; --i) { DxbcDest color_component_dest(DxbcDest::R(color_temp, 1 << i)); DxbcSrc color_component_src(DxbcSrc::R(color_temp).Select(i)); // Unpack the exponent to the temp. DxbcOpUBFE(temp1_dest, DxbcSrc::LU(3), DxbcSrc::LU(i * 10 + 7), packed_temp_low); // Unpack the mantissa to the result. DxbcOpUBFE(color_component_dest, DxbcSrc::LU(7), DxbcSrc::LU(i * 10), packed_temp_low); // Check if the number is denormalized. DxbcOpIf(false, temp1_src); { // Check if the number is non-zero (if the mantissa isn't zero - the // exponent is known to be zero at this point). DxbcOpIf(true, color_component_src); { // Normalize the mantissa. // Note that HLSL firstbithigh(x) is compiled to DXBC like: // `x ? 31 - firstbit_hi(x) : -1` // (returns the index from the LSB, not the MSB, but -1 for zero too). // temp = firstbit_hi(mantissa) DxbcOpFirstBitHi(temp1_dest, color_component_src); // temp = 7 - (31 - firstbit_hi(mantissa)) // Or, if expanded: // temp = firstbit_hi(mantissa) - 24 DxbcOpIAdd(temp1_dest, temp1_src, DxbcSrc::LI(-24)); // mantissa = mantissa << (7 - firstbithigh(mantissa)) // AND 0x7F not needed after this - BFI will do it. DxbcOpIShL(color_component_dest, color_component_src, temp1_src); // Get the normalized exponent. // exponent = 1 - (7 - firstbithigh(mantissa)) DxbcOpIAdd(temp1_dest, DxbcSrc::LI(1), -temp1_src); } // The number is zero. DxbcOpElse(); { // Set the unbiased exponent to -124 for zero - 124 will be added // later, resulting in zero float32. DxbcOpMov(temp1_dest, DxbcSrc::LI(-124)); } // Close the non-zero check. DxbcOpEndIf(); } // Close the denormal check. DxbcOpEndIf(); // Bias the exponent and move it to the correct location in f32. DxbcOpIMAd(temp1_dest, temp1_src, DxbcSrc::LI(1 << 23), DxbcSrc::LI(124 << 23)); // Combine the mantissa and the exponent. DxbcOpBFI(color_component_dest, DxbcSrc::LU(7), DxbcSrc::LU(16), color_component_src, temp1_src); } } DxbcOpBreak(); // *************************************************************************** // k_16_16 // k_16_16_16_16 (64bpp) // *************************************************************************** for (uint32_t i = 0; i < 2; ++i) { DxbcOpCase(DxbcSrc::LU(ROV_AddColorFormatFlags( i ? xenos::ColorRenderTargetFormat::k_16_16_16_16 : xenos::ColorRenderTargetFormat::k_16_16))); DxbcDest color_components_dest( DxbcDest::R(color_temp, i ? 0b1111 : 0b0011)); // Unpack the components. DxbcOpIBFE(color_components_dest, DxbcSrc::LU(16), DxbcSrc::LU(0, 16, 0, 16), DxbcSrc::R(packed_temp, 0b01010000 + packed_temp_components * 0b01010101)); // Convert from fixed-point. DxbcOpIToF(color_components_dest, DxbcSrc::R(color_temp)); // Normalize. DxbcOpMul(color_components_dest, DxbcSrc::R(color_temp), DxbcSrc::LF(32.0f / 32767.0f)); DxbcOpBreak(); } // *************************************************************************** // k_16_16_FLOAT // k_16_16_16_16_FLOAT (64bpp) // *************************************************************************** for (uint32_t i = 0; i < 2; ++i) { DxbcOpCase(DxbcSrc::LU(ROV_AddColorFormatFlags( i ? xenos::ColorRenderTargetFormat::k_16_16_16_16_FLOAT : xenos::ColorRenderTargetFormat::k_16_16_FLOAT))); DxbcDest color_components_dest( DxbcDest::R(color_temp, i ? 0b1111 : 0b0011)); // Unpack the components. DxbcOpUBFE(color_components_dest, DxbcSrc::LU(16), DxbcSrc::LU(0, 16, 0, 16), DxbcSrc::R(packed_temp, 0b01010000 + packed_temp_components * 0b01010101)); // Convert from 16-bit float. DxbcOpF16ToF32(color_components_dest, DxbcSrc::R(color_temp)); DxbcOpBreak(); } if (packed_temp != color_temp) { // Assume k_32_FLOAT or k_32_32_FLOAT for the rest. DxbcOpDefault(); DxbcOpMov( DxbcDest::R(color_temp, 0b0011), DxbcSrc::R(packed_temp, 0b0100 + packed_temp_components * 0b0101)); DxbcOpBreak(); } DxbcOpEndSwitch(); } void DxbcShaderTranslator::ROV_PackPreClampedColor( uint32_t rt_index, uint32_t color_temp, uint32_t packed_temp, uint32_t packed_temp_components, uint32_t temp1, uint32_t temp1_component, uint32_t temp2, uint32_t temp2_component) { // Packing normalized formats according to the Direct3D 11.3 functional // specification, but assuming clamping was done by the caller. assert_true(color_temp != packed_temp || packed_temp_components == 0); DxbcDest packed_dest_low( DxbcDest::R(packed_temp, 1 << packed_temp_components)); DxbcSrc packed_src_low( DxbcSrc::R(packed_temp).Select(packed_temp_components)); DxbcDest temp1_dest(DxbcDest::R(temp1, 1 << temp1_component)); DxbcSrc temp1_src(DxbcSrc::R(temp1).Select(temp1_component)); DxbcDest temp2_dest(DxbcDest::R(temp2, 1 << temp2_component)); DxbcSrc temp2_src(DxbcSrc::R(temp2).Select(temp2_component)); // Break register dependency after 32bpp cases. DxbcOpMov(DxbcDest::R(packed_temp, 1 << (packed_temp_components + 1)), DxbcSrc::LU(0)); // Choose the packing based on the render target's format. system_constants_used_ |= 1ull << kSysConst_EdramRTFormatFlags_Index; DxbcOpSwitch(DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_EdramRTFormatFlags_Vec) .Select(rt_index)); // *************************************************************************** // k_8_8_8_8 // k_8_8_8_8_GAMMA // *************************************************************************** for (uint32_t i = 0; i < 2; ++i) { DxbcOpCase(DxbcSrc::LU(ROV_AddColorFormatFlags( i ? xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA : xenos::ColorRenderTargetFormat::k_8_8_8_8))); for (uint32_t j = 0; j < 4; ++j) { if (i && j < 3) { ConvertPWLGamma(true, color_temp, j, temp1, temp1_component, temp1, temp1_component, temp2, temp2_component); // Denormalize and add 0.5 for rounding. DxbcOpMAd(temp1_dest, temp1_src, DxbcSrc::LF(255.0f), DxbcSrc::LF(0.5f)); } else { // Denormalize and add 0.5 for rounding. DxbcOpMAd(temp1_dest, DxbcSrc::R(color_temp).Select(j), DxbcSrc::LF(255.0f), DxbcSrc::LF(0.5f)); } // Convert to fixed-point. DxbcOpFToU(j ? temp1_dest : packed_dest_low, temp1_src); // Pack the upper components. if (j) { DxbcOpBFI(packed_dest_low, DxbcSrc::LU(8), DxbcSrc::LU(j * 8), temp1_src, packed_src_low); } } DxbcOpBreak(); } // *************************************************************************** // k_2_10_10_10 // k_2_10_10_10_AS_10_10_10_10 // *************************************************************************** DxbcOpCase(DxbcSrc::LU( ROV_AddColorFormatFlags(xenos::ColorRenderTargetFormat::k_2_10_10_10))); DxbcOpCase(DxbcSrc::LU(ROV_AddColorFormatFlags( xenos::ColorRenderTargetFormat::k_2_10_10_10_AS_10_10_10_10))); for (uint32_t i = 0; i < 4; ++i) { // Denormalize and convert to fixed-point. DxbcOpMAd(temp1_dest, DxbcSrc::R(color_temp).Select(i), DxbcSrc::LF(i < 3 ? 1023.0f : 3.0f), DxbcSrc::LF(0.5f)); DxbcOpFToU(i ? temp1_dest : packed_dest_low, temp1_src); // Pack the upper components. if (i) { DxbcOpBFI(packed_dest_low, DxbcSrc::LU(i < 3 ? 10 : 2), DxbcSrc::LU(i * 10), temp1_src, packed_src_low); } } DxbcOpBreak(); // *************************************************************************** // k_2_10_10_10_FLOAT // k_2_10_10_10_FLOAT_AS_16_16_16_16 // https://github.com/Microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexConvert.cpp // *************************************************************************** DxbcOpCase(DxbcSrc::LU(ROV_AddColorFormatFlags( xenos::ColorRenderTargetFormat::k_2_10_10_10_FLOAT))); DxbcOpCase(DxbcSrc::LU(ROV_AddColorFormatFlags( xenos::ColorRenderTargetFormat::k_2_10_10_10_FLOAT_AS_16_16_16_16))); { for (uint32_t i = 0; i < 3; ++i) { DxbcSrc color_component_src(DxbcSrc::R(color_temp).Select(i)); // Check if the number is too small to be represented as normalized 7e3. // temp2 = f32 < 2^-2 DxbcOpULT(temp2_dest, color_component_src, DxbcSrc::LU(0x3E800000)); // Handle denormalized numbers separately. DxbcOpIf(true, temp2_src); { // temp2 = f32 >> 23 DxbcOpUShR(temp2_dest, color_component_src, DxbcSrc::LU(23)); // temp2 = 125 - (f32 >> 23) DxbcOpIAdd(temp2_dest, DxbcSrc::LI(125), -temp2_src); // Don't allow the shift to overflow, since in DXBC the lower 5 bits of // the shift amount are used. // temp2 = min(125 - (f32 >> 23), 24) DxbcOpUMin(temp2_dest, temp2_src, DxbcSrc::LU(24)); // biased_f32 = (f32 & 0x7FFFFF) | 0x800000 DxbcOpBFI(temp1_dest, DxbcSrc::LU(9), DxbcSrc::LU(23), DxbcSrc::LU(1), color_component_src); // biased_f32 = // ((f32 & 0x7FFFFF) | 0x800000) >> min(125 - (f32 >> 23), 24) DxbcOpUShR(temp1_dest, temp1_src, temp2_src); } // Not denormalized? DxbcOpElse(); { // Bias the exponent. // biased_f32 = f32 + (-124 << 23) // (left shift of a negative value is undefined behavior) DxbcOpIAdd(temp1_dest, color_component_src, DxbcSrc::LU(0xC2000000u)); } // Close the denormal check. DxbcOpEndIf(); // Build the 7e3 number. // temp2 = (biased_f32 >> 16) & 1 DxbcOpUBFE(temp2_dest, DxbcSrc::LU(1), DxbcSrc::LU(16), temp1_src); // f10 = biased_f32 + 0x7FFF DxbcOpIAdd(temp1_dest, temp1_src, DxbcSrc::LU(0x7FFF)); // f10 = biased_f32 + 0x7FFF + ((biased_f32 >> 16) & 1) DxbcOpIAdd(temp1_dest, temp1_src, temp2_src); // f10 = ((biased_f32 + 0x7FFF + ((biased_f32 >> 16) & 1)) >> 16) & 0x3FF DxbcOpUBFE(i ? temp1_dest : packed_dest_low, DxbcSrc::LU(10), DxbcSrc::LU(16), temp1_src); // Pack the upper components. if (i) { DxbcOpBFI(packed_dest_low, DxbcSrc::LU(10), DxbcSrc::LU(i * 10), temp1_src, packed_src_low); } } // Denormalize the alpha and convert it to fixed-point. DxbcOpMAd(temp1_dest, DxbcSrc::R(color_temp, DxbcSrc::kWWWW), DxbcSrc::LF(3.0f), DxbcSrc::LF(0.5f)); DxbcOpFToU(temp1_dest, temp1_src); // Pack the alpha. DxbcOpBFI(packed_dest_low, DxbcSrc::LU(2), DxbcSrc::LU(30), temp1_src, packed_src_low); } DxbcOpBreak(); // *************************************************************************** // k_16_16 // k_16_16_16_16 (64bpp) // *************************************************************************** for (uint32_t i = 0; i < 2; ++i) { DxbcOpCase(DxbcSrc::LU(ROV_AddColorFormatFlags( i ? xenos::ColorRenderTargetFormat::k_16_16_16_16 : xenos::ColorRenderTargetFormat::k_16_16))); for (uint32_t j = 0; j < (uint32_t(2) << i); ++j) { // Denormalize and convert to fixed-point, making 0.5 with the proper sign // in temp2. DxbcOpGE(temp2_dest, DxbcSrc::R(color_temp).Select(j), DxbcSrc::LF(0.0f)); DxbcOpMovC(temp2_dest, temp2_src, DxbcSrc::LF(0.5f), DxbcSrc::LF(-0.5f)); DxbcOpMAd(temp1_dest, DxbcSrc::R(color_temp).Select(j), DxbcSrc::LF(32767.0f / 32.0f), temp2_src); DxbcDest packed_dest_half( DxbcDest::R(packed_temp, 1 << (packed_temp_components + (j >> 1)))); // Convert to fixed-point. DxbcOpFToI((j & 1) ? temp1_dest : packed_dest_half, temp1_src); // Pack green or alpha. if (j & 1) { DxbcOpBFI( packed_dest_half, DxbcSrc::LU(16), DxbcSrc::LU(16), temp1_src, DxbcSrc::R(packed_temp).Select(packed_temp_components + (j >> 1))); } } DxbcOpBreak(); } // *************************************************************************** // k_16_16_FLOAT // k_16_16_16_16_FLOAT (64bpp) // *************************************************************************** for (uint32_t i = 0; i < 2; ++i) { DxbcOpCase(DxbcSrc::LU(ROV_AddColorFormatFlags( i ? xenos::ColorRenderTargetFormat::k_16_16_16_16_FLOAT : xenos::ColorRenderTargetFormat::k_16_16_FLOAT))); for (uint32_t j = 0; j < (uint32_t(2) << i); ++j) { DxbcDest packed_dest_half( DxbcDest::R(packed_temp, 1 << (packed_temp_components + (j >> 1)))); // Convert to 16-bit float. DxbcOpF32ToF16((j & 1) ? temp1_dest : packed_dest_half, DxbcSrc::R(color_temp).Select(j)); // Pack green or alpha. if (j & 1) { DxbcOpBFI( packed_dest_half, DxbcSrc::LU(16), DxbcSrc::LU(16), temp1_src, DxbcSrc::R(packed_temp).Select(packed_temp_components + (j >> 1))); } } DxbcOpBreak(); } if (packed_temp != color_temp) { // Assume k_32_FLOAT or k_32_32_FLOAT for the rest. DxbcOpDefault(); DxbcOpMov(DxbcDest::R(packed_temp, 0b11 << packed_temp_components), DxbcSrc::R(color_temp, 0b0100 << (packed_temp_components * 2))); DxbcOpBreak(); } DxbcOpEndSwitch(); } void DxbcShaderTranslator::ROV_HandleColorBlendFactorCases( uint32_t src_temp, uint32_t dst_temp, uint32_t factor_temp) { DxbcDest factor_dest(DxbcDest::R(factor_temp, 0b0111)); DxbcSrc one_src(DxbcSrc::LF(1.0f)); // kOne. DxbcOpCase(DxbcSrc::LU(uint32_t(xenos::BlendFactor::kOne))); DxbcOpMov(factor_dest, one_src); DxbcOpBreak(); // kSrcColor DxbcOpCase(DxbcSrc::LU(uint32_t(xenos::BlendFactor::kSrcColor))); if (factor_temp != src_temp) { DxbcOpMov(factor_dest, DxbcSrc::R(src_temp)); } DxbcOpBreak(); // kOneMinusSrcColor DxbcOpCase(DxbcSrc::LU(uint32_t(xenos::BlendFactor::kOneMinusSrcColor))); DxbcOpAdd(factor_dest, one_src, -DxbcSrc::R(src_temp)); DxbcOpBreak(); // kSrcAlpha DxbcOpCase(DxbcSrc::LU(uint32_t(xenos::BlendFactor::kSrcAlpha))); DxbcOpMov(factor_dest, DxbcSrc::R(src_temp, DxbcSrc::kWWWW)); DxbcOpBreak(); // kOneMinusSrcAlpha DxbcOpCase(DxbcSrc::LU(uint32_t(xenos::BlendFactor::kOneMinusSrcAlpha))); DxbcOpAdd(factor_dest, one_src, -DxbcSrc::R(src_temp, DxbcSrc::kWWWW)); DxbcOpBreak(); // kDstColor DxbcOpCase(DxbcSrc::LU(uint32_t(xenos::BlendFactor::kDstColor))); if (factor_temp != dst_temp) { DxbcOpMov(factor_dest, DxbcSrc::R(dst_temp)); } DxbcOpBreak(); // kOneMinusDstColor DxbcOpCase(DxbcSrc::LU(uint32_t(xenos::BlendFactor::kOneMinusDstColor))); DxbcOpAdd(factor_dest, one_src, -DxbcSrc::R(dst_temp)); DxbcOpBreak(); // kDstAlpha DxbcOpCase(DxbcSrc::LU(uint32_t(xenos::BlendFactor::kDstAlpha))); DxbcOpMov(factor_dest, DxbcSrc::R(dst_temp, DxbcSrc::kWWWW)); DxbcOpBreak(); // kOneMinusDstAlpha DxbcOpCase(DxbcSrc::LU(uint32_t(xenos::BlendFactor::kOneMinusDstAlpha))); DxbcOpAdd(factor_dest, one_src, -DxbcSrc::R(dst_temp, DxbcSrc::kWWWW)); DxbcOpBreak(); // Factors involving the constant. system_constants_used_ |= 1ull << kSysConst_EdramBlendConstant_Index; // kConstantColor DxbcOpCase(DxbcSrc::LU(uint32_t(xenos::BlendFactor::kConstantColor))); DxbcOpMov(factor_dest, DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_EdramBlendConstant_Vec)); DxbcOpBreak(); // kOneMinusConstantColor DxbcOpCase(DxbcSrc::LU(uint32_t(xenos::BlendFactor::kOneMinusConstantColor))); DxbcOpAdd(factor_dest, one_src, -DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_EdramBlendConstant_Vec)); DxbcOpBreak(); // kConstantAlpha DxbcOpCase(DxbcSrc::LU(uint32_t(xenos::BlendFactor::kConstantAlpha))); DxbcOpMov(factor_dest, DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_EdramBlendConstant_Vec, DxbcSrc::kWWWW)); DxbcOpBreak(); // kOneMinusConstantAlpha DxbcOpCase(DxbcSrc::LU(uint32_t(xenos::BlendFactor::kOneMinusConstantAlpha))); DxbcOpAdd(factor_dest, one_src, -DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_EdramBlendConstant_Vec, DxbcSrc::kWWWW)); DxbcOpBreak(); // kSrcAlphaSaturate DxbcOpCase(DxbcSrc::LU(uint32_t(xenos::BlendFactor::kSrcAlphaSaturate))); DxbcOpAdd(DxbcDest::R(factor_temp, 0b0001), one_src, -DxbcSrc::R(dst_temp, DxbcSrc::kWWWW)); DxbcOpMin(factor_dest, DxbcSrc::R(src_temp, DxbcSrc::kWWWW), DxbcSrc::R(factor_temp, DxbcSrc::kXXXX)); DxbcOpBreak(); // kZero default. DxbcOpDefault(); DxbcOpMov(factor_dest, DxbcSrc::LF(0.0f)); DxbcOpBreak(); } void DxbcShaderTranslator::ROV_HandleAlphaBlendFactorCases( uint32_t src_temp, uint32_t dst_temp, uint32_t factor_temp, uint32_t factor_component) { DxbcDest factor_dest(DxbcDest::R(factor_temp, 1 << factor_component)); DxbcSrc one_src(DxbcSrc::LF(1.0f)); // kOne, kSrcAlphaSaturate. DxbcOpCase(DxbcSrc::LU(uint32_t(xenos::BlendFactor::kOne))); DxbcOpCase(DxbcSrc::LU(uint32_t(xenos::BlendFactor::kSrcAlphaSaturate))); DxbcOpMov(factor_dest, one_src); DxbcOpBreak(); // kSrcColor, kSrcAlpha. DxbcOpCase(DxbcSrc::LU(uint32_t(xenos::BlendFactor::kSrcColor))); DxbcOpCase(DxbcSrc::LU(uint32_t(xenos::BlendFactor::kSrcAlpha))); if (factor_temp != src_temp || factor_component != 3) { DxbcOpMov(factor_dest, DxbcSrc::R(src_temp, DxbcSrc::kWWWW)); } DxbcOpBreak(); // kOneMinusSrcColor, kOneMinusSrcAlpha. DxbcOpCase(DxbcSrc::LU(uint32_t(xenos::BlendFactor::kOneMinusSrcColor))); DxbcOpCase(DxbcSrc::LU(uint32_t(xenos::BlendFactor::kOneMinusSrcAlpha))); DxbcOpAdd(factor_dest, one_src, -DxbcSrc::R(src_temp, DxbcSrc::kWWWW)); DxbcOpBreak(); // kDstColor, kDstAlpha. DxbcOpCase(DxbcSrc::LU(uint32_t(xenos::BlendFactor::kDstColor))); DxbcOpCase(DxbcSrc::LU(uint32_t(xenos::BlendFactor::kDstAlpha))); if (factor_temp != dst_temp || factor_component != 3) { DxbcOpMov(factor_dest, DxbcSrc::R(dst_temp, DxbcSrc::kWWWW)); } DxbcOpBreak(); // kOneMinusDstColor, kOneMinusDstAlpha. DxbcOpCase(DxbcSrc::LU(uint32_t(xenos::BlendFactor::kOneMinusDstColor))); DxbcOpCase(DxbcSrc::LU(uint32_t(xenos::BlendFactor::kOneMinusDstAlpha))); DxbcOpAdd(factor_dest, one_src, -DxbcSrc::R(dst_temp, DxbcSrc::kWWWW)); DxbcOpBreak(); // Factors involving the constant. system_constants_used_ |= 1ull << kSysConst_EdramBlendConstant_Index; // kConstantColor, kConstantAlpha. DxbcOpCase(DxbcSrc::LU(uint32_t(xenos::BlendFactor::kConstantColor))); DxbcOpCase(DxbcSrc::LU(uint32_t(xenos::BlendFactor::kConstantAlpha))); DxbcOpMov(factor_dest, DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_EdramBlendConstant_Vec, DxbcSrc::kWWWW)); DxbcOpBreak(); // kOneMinusConstantColor, kOneMinusConstantAlpha. DxbcOpCase(DxbcSrc::LU(uint32_t(xenos::BlendFactor::kOneMinusConstantColor))); DxbcOpCase(DxbcSrc::LU(uint32_t(xenos::BlendFactor::kOneMinusConstantAlpha))); DxbcOpAdd(factor_dest, one_src, -DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_EdramBlendConstant_Vec, DxbcSrc::kWWWW)); DxbcOpBreak(); // kZero default. DxbcOpDefault(); DxbcOpMov(factor_dest, DxbcSrc::LF(0.0f)); DxbcOpBreak(); } void DxbcShaderTranslator::CompletePixelShader_WriteToRTVs_AlphaToMask() { // Check if alpha to coverage can be done at all in this shader. if (!writes_color_target(0)) { return; } // Check if alpha to coverage is enabled. system_constants_used_ |= 1ull << kSysConst_AlphaToMask_Index; DxbcOpIf(true, DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_AlphaToMask_Vec) .Select(kSysConst_AlphaToMask_Comp)); uint32_t temp = PushSystemTemp(); DxbcDest temp_x_dest(DxbcDest::R(temp, 0b0001)); DxbcSrc temp_x_src(DxbcSrc::R(temp, DxbcSrc::kXXXX)); DxbcDest temp_y_dest(DxbcDest::R(temp, 0b0010)); DxbcSrc temp_y_src(DxbcSrc::R(temp, DxbcSrc::kYYYY)); DxbcDest temp_z_dest(DxbcDest::R(temp, 0b0100)); DxbcSrc temp_z_src(DxbcSrc::R(temp, DxbcSrc::kZZZZ)); // Convert SSAA sample position to integer to temp.xy (not caring about the // resolution scale because it's not supported anywhere on the RTV output // path). in_position_xy_used_ = true; DxbcOpFToU(DxbcDest::R(temp, 0b0011), DxbcSrc::V(uint32_t(InOutRegister::kPSInPosition))); // Check if SSAA is enabled. system_constants_used_ |= 1ull << kSysConst_SampleCountLog2_Index; DxbcOpIf(true, DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_SampleCountLog2_Vec) .Select(kSysConst_SampleCountLog2_Comp + 1)); { // Check if SSAA is 4x or 2x. system_constants_used_ |= 1ull << kSysConst_SampleCountLog2_Index; DxbcOpIf(true, DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_SampleCountLog2_Vec) .Select(kSysConst_SampleCountLog2_Comp)); { // 4x SSAA. // Build the sample index in temp.z where X is the low bit and Y is the // high bit, for calculation of the dithering base according to the sample // position (left/right and top/bottom). DxbcOpAnd(temp_z_dest, temp_y_src, DxbcSrc::LU(1)); DxbcOpBFI(temp_z_dest, DxbcSrc::LU(31), DxbcSrc::LU(1), temp_z_src, temp_x_src); // Top-left sample base: 0.75. // Top-right sample base: 0.5. // Bottom-left sample base: 0.25. // Bottom-right sample base: 1.0. // The threshold would be 1 - frac(0.25 + 0.25 * (x | (y << 1))) - offset. // Multiplication here will result in exactly 1 (power of 2 multiplied by // an integer). // Calculate the base. DxbcOpUToF(temp_z_dest, temp_z_src); DxbcOpMAd(temp_z_dest, temp_z_src, DxbcSrc::LF(0.25f), DxbcSrc::LF(0.25f)); DxbcOpFrc(temp_z_dest, temp_z_src); DxbcOpAdd(temp_z_dest, DxbcSrc::LF(1.0f), -temp_z_src); // Get the dithering threshold offset index for the guest pixel to temp.x, // Y - low bit of offset index, X - high bit. DxbcOpUBFE(DxbcDest::R(temp, 0b0011), DxbcSrc::LU(1), DxbcSrc::LU(1), DxbcSrc::R(temp)); DxbcOpBFI(temp_x_dest, DxbcSrc::LU(1), DxbcSrc::LU(1), temp_x_src, temp_y_src); // Write the offset scale to temp.y. DxbcOpMov(temp_y_dest, DxbcSrc::LF(-1.0f / 16.0f)); } DxbcOpElse(); { // 2x SSAA. // Check if the top (base 0.5) or the bottom (base 1.0) sample to temp.z, // and also extract the guest pixel Y parity to temp.y. DxbcOpUBFE(DxbcDest::R(temp, 0b0110), DxbcSrc::LU(1), DxbcSrc::LU(0, 1, 0, 0), temp_y_src); DxbcOpMovC(temp_z_dest, temp_z_src, DxbcSrc::LF(1.0f), DxbcSrc::LF(0.5f)); // Get the dithering threshold offset index for the guest pixel to temp.x, // Y - low bit of offset index, X - high bit. DxbcOpBFI(temp_x_dest, DxbcSrc::LU(1), DxbcSrc::LU(1), temp_x_src, temp_y_src); // Write the offset scale to temp.y. DxbcOpMov(temp_y_dest, DxbcSrc::LF(-1.0f / 8.0f)); } // Close the 4x check. DxbcOpEndIf(); } // SSAA is disabled. DxbcOpElse(); { // Write the base 1.0 to temp.z. DxbcOpMov(temp_z_dest, DxbcSrc::LF(1.0f)); // Get the dithering threshold offset index for the guest pixel to temp.x, // Y - low bit of offset index, X - high bit. DxbcOpAnd(temp_y_dest, temp_y_src, DxbcSrc::LU(1)); DxbcOpBFI(temp_x_dest, DxbcSrc::LU(1), DxbcSrc::LU(1), temp_x_src, temp_y_src); // Write the offset scale to temp.y. DxbcOpMov(temp_y_dest, DxbcSrc::LF(-1.0f / 4.0f)); } // Close the 2x/4x check. DxbcOpEndIf(); // Extract the dithering offset to temp.x for the quad pixel index. DxbcOpIShL(temp_x_dest, temp_x_src, DxbcSrc::LU(1)); system_constants_used_ |= 1ull << kSysConst_AlphaToMask_Index; DxbcOpUBFE(temp_x_dest, DxbcSrc::LU(2), temp_x_src, DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_AlphaToMask_Vec) .Select(kSysConst_AlphaToMask_Comp)); DxbcOpUToF(temp_x_dest, temp_x_src); // Combine the base and the offset to temp.x. DxbcOpMAd(temp_x_dest, temp_x_src, temp_y_src, temp_z_src); // Check if alpha of oC0 is at or greater than the threshold (handling NaN // according to the Direct3D 11.3 functional specification, as not covered). DxbcOpGE(temp_x_dest, DxbcSrc::R(system_temps_color_[0], DxbcSrc::kWWWW), temp_x_src); // Discard the SSAA sample if it's not covered. DxbcOpDiscard(false, temp_x_src); // Release temp. PopSystemTemp(); // Close the alpha to coverage check. DxbcOpEndIf(); } void DxbcShaderTranslator::CompletePixelShader_WriteToRTVs() { if (!writes_any_color_target()) { return; } // Check if this sample needs to be discarded by alpha to coverage. CompletePixelShader_WriteToRTVs_AlphaToMask(); // Get the write mask as components, and also apply the exponent bias after // alpha to coverage because it needs the unbiased alpha from the shader. uint32_t guest_rt_mask = 0; for (uint32_t i = 0; i < 4; ++i) { if (!writes_color_target(i)) { continue; } guest_rt_mask |= 1 << i; system_constants_used_ |= 1ull << kSysConst_ColorExpBias_Index; DxbcOpMul(DxbcDest::R(system_temps_color_[i]), DxbcSrc::R(system_temps_color_[i]), DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_ColorExpBias_Vec) .Select(i)); } // Convert to gamma space - this is incorrect, since it must be done after // blending on the Xbox 360, but this is just one of many blending issues in // the RTV path. uint32_t gamma_temp = PushSystemTemp(); for (uint32_t i = 0; i < 4; ++i) { if (!(guest_rt_mask & (1 << i))) { continue; } system_constants_used_ |= 1ull << kSysConst_Flags_Index; DxbcOpAnd(DxbcDest::R(gamma_temp, 0b0001), DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_Flags_Vec) .Select(kSysConst_Flags_Comp), DxbcSrc::LU(kSysFlag_Color0Gamma << i)); DxbcOpIf(true, DxbcSrc::R(gamma_temp, DxbcSrc::kXXXX)); for (uint32_t j = 0; j < 3; ++j) { ConvertPWLGamma(true, system_temps_color_[i], j, system_temps_color_[i], j, gamma_temp, 0, gamma_temp, 1); } DxbcOpEndIf(); } // Release gamma_temp. PopSystemTemp(); // Remap guest render target indices to host since because on the host, the // indices of the bound render targets are consecutive. This is done using 16 // movc instructions because indexable temps are known to be causing // performance issues on some Nvidia GPUs. In the map, the components are host // render target indices, and the values are the guest ones. uint32_t remap_movc_mask_temp = PushSystemTemp(); uint32_t remap_movc_target_temp = PushSystemTemp(); system_constants_used_ |= 1ull << kSysConst_ColorOutputMap_Index; // Host RT i, guest RT j. for (uint32_t i = 0; i < 4; ++i) { // mask = map.iiii == (0, 1, 2, 3) DxbcOpIEq(DxbcDest::R(remap_movc_mask_temp, guest_rt_mask), DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_ColorOutputMap_Vec) .Select(i), DxbcSrc::LU(0, 1, 2, 3)); bool guest_rt_first = true; for (uint32_t j = 0; j < 4; ++j) { // If map.i == j, move guest color j to the temporary host color. if (!(guest_rt_mask & (1 << j))) { continue; } DxbcOpMovC(DxbcDest::R(remap_movc_target_temp), DxbcSrc::R(remap_movc_mask_temp).Select(j), DxbcSrc::R(system_temps_color_[j]), guest_rt_first ? DxbcSrc::LF(0.0f) : DxbcSrc::R(remap_movc_target_temp)); guest_rt_first = false; } // Write the remapped color to host render target i. DxbcOpMov(DxbcDest::O(i), DxbcSrc::R(remap_movc_target_temp)); } // Release remap_movc_mask_temp and remap_movc_target_temp. PopSystemTemp(2); } void DxbcShaderTranslator::CompletePixelShader_ROV_AlphaToMaskSample( uint32_t sample_index, float threshold_base, DxbcSrc threshold_offset, float threshold_offset_scale, uint32_t temp, uint32_t temp_component) { DxbcDest temp_dest(DxbcDest::R(temp, 1 << temp_component)); DxbcSrc temp_src(DxbcSrc::R(temp).Select(temp_component)); // Calculate the threshold. DxbcOpMAd(temp_dest, threshold_offset, DxbcSrc::LF(-threshold_offset_scale), DxbcSrc::LF(threshold_base)); // Check if alpha of oC0 is at or greater than the threshold (handling NaN // according to the Direct3D 11.3 functional specification, as not covered). DxbcOpGE(temp_dest, DxbcSrc::R(system_temps_color_[0], DxbcSrc::kWWWW), temp_src); // Keep all bits in system_temp_rov_params_.x but the ones that need to be // removed in case of failure (coverage and deferred depth/stencil write are // removed). DxbcOpOr(temp_dest, temp_src, DxbcSrc::LU(~(uint32_t(0b00010001) << sample_index))); // Clear the coverage for samples that have failed the test. DxbcOpAnd(DxbcDest::R(system_temp_rov_params_, 0b0001), DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kXXXX), temp_src); } void DxbcShaderTranslator::CompletePixelShader_ROV_AlphaToMask() { // Check if alpha to coverage can be done at all in this shader. if (!writes_color_target(0)) { return; } // Check if alpha to coverage is enabled. system_constants_used_ |= 1ull << kSysConst_AlphaToMask_Index; DxbcOpIf(true, DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_AlphaToMask_Vec) .Select(kSysConst_AlphaToMask_Comp)); uint32_t temp = PushSystemTemp(); DxbcDest temp_x_dest(DxbcDest::R(temp, 0b0001)); DxbcSrc temp_x_src(DxbcSrc::R(temp, DxbcSrc::kXXXX)); // Get the dithering threshold offset index for the pixel, Y - low bit of // offset index, X - high bit, and extract the offset and convert it to // floating-point. With resolution scaling, still using host pixels, to // preserve the idea of dithering. // temp.x = alpha to coverage offset as float 0.0...3.0. in_position_xy_used_ = true; DxbcOpFToU(DxbcDest::R(temp, 0b0011), DxbcSrc::V(uint32_t(InOutRegister::kPSInPosition))); DxbcOpAnd(DxbcDest::R(temp, 0b0010), DxbcSrc::R(temp, DxbcSrc::kYYYY), DxbcSrc::LU(1)); DxbcOpBFI(temp_x_dest, DxbcSrc::LU(1), DxbcSrc::LU(1), temp_x_src, DxbcSrc::R(temp, DxbcSrc::kYYYY)); DxbcOpIShL(temp_x_dest, temp_x_src, DxbcSrc::LU(1)); system_constants_used_ |= 1ull << kSysConst_AlphaToMask_Index; DxbcOpUBFE(temp_x_dest, DxbcSrc::LU(2), temp_x_src, DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_AlphaToMask_Vec) .Select(kSysConst_AlphaToMask_Comp)); DxbcOpUToF(temp_x_dest, temp_x_src); // The test must effect not only the coverage bits, but also the deferred // depth/stencil write bits since the coverage is zeroed for samples that have // failed the depth/stencil test, but stencil may still require writing - but // if the sample is discarded by alpha to coverage, it must not be written at // all. // Check if MSAA is enabled. system_constants_used_ |= 1ull << kSysConst_SampleCountLog2_Index; DxbcOpIf(true, DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_SampleCountLog2_Vec) .Select(kSysConst_SampleCountLog2_Comp + 1)); { // Check if MSAA is 4x or 2x. system_constants_used_ |= 1ull << kSysConst_SampleCountLog2_Index; DxbcOpIf(true, DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_SampleCountLog2_Vec) .Select(kSysConst_SampleCountLog2_Comp)); // 4x MSAA. CompletePixelShader_ROV_AlphaToMaskSample(0, 0.75f, temp_x_src, 1.0f / 16.0f, temp, 1); CompletePixelShader_ROV_AlphaToMaskSample(1, 0.25f, temp_x_src, 1.0f / 16.0f, temp, 1); CompletePixelShader_ROV_AlphaToMaskSample(2, 0.5f, temp_x_src, 1.0f / 16.0f, temp, 1); CompletePixelShader_ROV_AlphaToMaskSample(3, 1.0f, temp_x_src, 1.0f / 16.0f, temp, 1); // 2x MSAA. DxbcOpElse(); CompletePixelShader_ROV_AlphaToMaskSample(0, 0.5f, temp_x_src, 1.0f / 8.0f, temp, 1); CompletePixelShader_ROV_AlphaToMaskSample(1, 1.0f, temp_x_src, 1.0f / 8.0f, temp, 1); // Close the 4x check. DxbcOpEndIf(); } // MSAA is disabled. DxbcOpElse(); CompletePixelShader_ROV_AlphaToMaskSample(0, 1.0f, temp_x_src, 1.0f / 4.0f, temp, 1); // Close the 2x/4x check. DxbcOpEndIf(); // Check if any sample is still covered (the mask includes both 0:3 and 4:7 // parts because there may be samples which passed alpha to coverage, but not // stencil test, and the stencil buffer needs to be modified - in this case, // samples would be dropped in 0:3, but not in 4:7). DxbcOpAnd(temp_x_dest, DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kXXXX), DxbcSrc::LU(0b11111111)); DxbcOpRetC(false, temp_x_src); // Release temp. PopSystemTemp(); // Close the alpha to coverage check. DxbcOpEndIf(); } void DxbcShaderTranslator::CompletePixelShader_WriteToROV() { // Discard samples with alpha to coverage. CompletePixelShader_ROV_AlphaToMask(); uint32_t temp = PushSystemTemp(); DxbcDest temp_x_dest(DxbcDest::R(temp, 0b0001)); DxbcSrc temp_x_src(DxbcSrc::R(temp, DxbcSrc::kXXXX)); DxbcDest temp_y_dest(DxbcDest::R(temp, 0b0010)); DxbcSrc temp_y_src(DxbcSrc::R(temp, DxbcSrc::kYYYY)); DxbcDest temp_z_dest(DxbcDest::R(temp, 0b0100)); DxbcSrc temp_z_src(DxbcSrc::R(temp, DxbcSrc::kZZZZ)); DxbcDest temp_w_dest(DxbcDest::R(temp, 0b1000)); DxbcSrc temp_w_src(DxbcSrc::R(temp, DxbcSrc::kWWWW)); // Do late depth/stencil test (which includes writing) if needed or deferred // depth writing. if (ROV_IsDepthStencilEarly()) { // Write modified depth/stencil. for (uint32_t i = 0; i < 4; ++i) { // Get if need to write to temp.x. // temp.x = whether the depth sample needs to be written. DxbcOpAnd(temp_x_dest, DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kXXXX), DxbcSrc::LU(1 << (4 + i))); // Check if need to write. // temp.x = free. DxbcOpIf(true, temp_x_src); { // Write the new depth/stencil. if (uav_index_edram_ == kBindingIndexUnallocated) { uav_index_edram_ = uav_count_++; } DxbcOpStoreUAVTyped( DxbcDest::U(uav_index_edram_, uint32_t(UAVRegister::kEdram)), DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kYYYY), 1, DxbcSrc::R(system_temp_rov_depth_stencil_).Select(i)); } // Close the write check. DxbcOpEndIf(); // Go to the next sample (samples are at +0, +80, +1, +81, so need to do // +80, -79, +80 and -81 after each sample). if (i < 3) { system_constants_used_ |= 1ull << kSysConst_EdramResolutionSquareScale_Index; DxbcOpIMAd(DxbcDest::R(system_temp_rov_params_, 0b0010), DxbcSrc::LI((i & 1) ? -78 - i : 80), DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_EdramResolutionSquareScale_Vec) .Select(kSysConst_EdramResolutionSquareScale_Comp), DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kYYYY)); } } } else { ROV_DepthStencilTest(); } if (!is_depth_only_pixel_shader_) { // Check if any sample is still covered after depth testing and writing, // skip color writing completely in this case. // temp.x = whether any sample is still covered. DxbcOpAnd(temp_x_dest, DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kXXXX), DxbcSrc::LU(0b1111)); // temp.x = free. DxbcOpRetC(false, temp_x_src); } // Write color values. for (uint32_t i = 0; i < 4; ++i) { if (!writes_color_target(i)) { continue; } DxbcSrc keep_mask_vec_src( DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_EdramRTKeepMask_Vec + (i >> 1))); uint32_t keep_mask_component = (i & 1) * 2; uint32_t keep_mask_swizzle = keep_mask_component * 0b0101 + 0b0100; // Check if color writing is disabled - special keep mask constant case, // both 32bpp parts are forced UINT32_MAX, but also check whether the shader // has written anything to this target at all. // Combine both parts of the keep mask to check if both are 0xFFFFFFFF. // temp.x = whether all bits need to be kept. system_constants_used_ |= 1ull << kSysConst_EdramRTKeepMask_Index; DxbcOpAnd(temp_x_dest, keep_mask_vec_src.Select(keep_mask_component), keep_mask_vec_src.Select(keep_mask_component + 1)); // Flip the bits so both UINT32_MAX would result in 0 - not writing. // temp.x = whether any bits need to be written. DxbcOpNot(temp_x_dest, temp_x_src); // Get the bits that will be used for checking wherther the render target // has been written to on the taken execution path - if the write mask is // empty, AND zero with the test bit to always get zero. // temp.x = bits for checking whether the render target has been written to. DxbcOpMovC(temp_x_dest, temp_x_src, DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kXXXX), DxbcSrc::LU(0)); // Check if the render target was written to on the execution path. // temp.x = whether anything was written and needs to be stored. DxbcOpAnd(temp_x_dest, temp_x_src, DxbcSrc::LU(1 << (8 + i))); // Check if need to write anything to the render target. // temp.x = free. DxbcOpIf(true, temp_x_src); // Apply the exponent bias after alpha to coverage because it needs the // unbiased alpha from the shader. system_constants_used_ |= 1ull << kSysConst_ColorExpBias_Index; DxbcOpMul(DxbcDest::R(system_temps_color_[i]), DxbcSrc::R(system_temps_color_[i]), DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_ColorExpBias_Vec) .Select(i)); // Add the EDRAM bases of the render target to system_temp_rov_params_.zw. system_constants_used_ |= 1ull << kSysConst_EdramRTBaseDwordsScaled_Index; DxbcOpIAdd(DxbcDest::R(system_temp_rov_params_, 0b1100), DxbcSrc::R(system_temp_rov_params_), DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_EdramRTBaseDwordsScaled_Vec) .Select(i)); DxbcSrc rt_blend_factors_ops_src( DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_EdramRTBlendFactorsOps_Vec) .Select(i)); DxbcSrc rt_clamp_vec_src( DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_EdramRTClamp_Vec + i)); // Get if not blending to pack the color once for all 4 samples. // temp.x = whether blending is disabled. system_constants_used_ |= 1ull << kSysConst_EdramRTBlendFactorsOps_Index; DxbcOpIEq(temp_x_dest, rt_blend_factors_ops_src, DxbcSrc::LU(0x00010001)); // Check if not blending. // temp.x = free. DxbcOpIf(true, temp_x_src); { // Clamp the color to the render target's representable range - will be // packed. system_constants_used_ |= 1ull << kSysConst_EdramRTClamp_Index; DxbcOpMax(DxbcDest::R(system_temps_color_[i]), DxbcSrc::R(system_temps_color_[i]), rt_clamp_vec_src.Swizzle(0b01000000)); DxbcOpMin(DxbcDest::R(system_temps_color_[i]), DxbcSrc::R(system_temps_color_[i]), rt_clamp_vec_src.Swizzle(0b11101010)); // Pack the color once if blending. // temp.xy = packed color. ROV_PackPreClampedColor(i, system_temps_color_[i], temp, 0, temp, 2, temp, 3); } // Blending is enabled. DxbcOpElse(); { // Get if the blending source color is fixed-point for clamping if it is. // temp.x = whether color is fixed-point. system_constants_used_ |= 1ull << kSysConst_EdramRTFormatFlags_Index; DxbcOpAnd(temp_x_dest, DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_EdramRTFormatFlags_Vec) .Select(i), DxbcSrc::LU(kRTFormatFlag_FixedPointColor)); // Check if the blending source color is fixed-point and needs clamping. // temp.x = free. DxbcOpIf(true, temp_x_src); { // Clamp the blending source color if needed. system_constants_used_ |= 1ull << kSysConst_EdramRTClamp_Index; DxbcOpMax(DxbcDest::R(system_temps_color_[i], 0b0111), DxbcSrc::R(system_temps_color_[i]), rt_clamp_vec_src.Select(0)); DxbcOpMin(DxbcDest::R(system_temps_color_[i], 0b0111), DxbcSrc::R(system_temps_color_[i]), rt_clamp_vec_src.Select(2)); } // Close the fixed-point color check. DxbcOpEndIf(); // Get if the blending source alpha is fixed-point for clamping if it is. // temp.x = whether alpha is fixed-point. system_constants_used_ |= 1ull << kSysConst_EdramRTFormatFlags_Index; DxbcOpAnd(temp_x_dest, DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_EdramRTFormatFlags_Vec) .Select(i), DxbcSrc::LU(kRTFormatFlag_FixedPointAlpha)); // Check if the blending source alpha is fixed-point and needs clamping. // temp.x = free. DxbcOpIf(true, temp_x_src); { // Clamp the blending source alpha if needed. system_constants_used_ |= 1ull << kSysConst_EdramRTClamp_Index; DxbcOpMax(DxbcDest::R(system_temps_color_[i], 0b1000), DxbcSrc::R(system_temps_color_[i], DxbcSrc::kWWWW), rt_clamp_vec_src.Select(1)); DxbcOpMin(DxbcDest::R(system_temps_color_[i], 0b1000), DxbcSrc::R(system_temps_color_[i], DxbcSrc::kWWWW), rt_clamp_vec_src.Select(3)); } // Close the fixed-point alpha check. DxbcOpEndIf(); // Break register dependency in the color sample raster operation. // temp.xy = 0 instead of packed color. DxbcOpMov(DxbcDest::R(temp, 0b0011), DxbcSrc::LU(0)); } DxbcOpEndIf(); DxbcSrc rt_format_flags_src( DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_EdramRTFormatFlags_Vec) .Select(i)); // Blend, mask and write all samples. for (uint32_t j = 0; j < 4; ++j) { // Get if the sample is covered. // temp.z = whether the sample is covered. DxbcOpAnd(temp_z_dest, DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kXXXX), DxbcSrc::LU(1 << j)); // Check if the sample is covered. // temp.z = free. DxbcOpIf(true, temp_z_src); // Only temp.xy are used at this point (containing the packed color from // the shader if not blending). // *********************************************************************** // Color sample raster operation. // *********************************************************************** // *********************************************************************** // Checking if color loading must be done - if any component needs to be // kept or if blending is enabled. // *********************************************************************** // Get if need to keep any components to temp.z. // temp.z = whether any components must be kept (OR of keep masks). system_constants_used_ |= 1ull << kSysConst_EdramRTKeepMask_Index; DxbcOpOr(temp_z_dest, keep_mask_vec_src.Select(keep_mask_component), keep_mask_vec_src.Select(keep_mask_component + 1)); // Blending isn't done if it's 1 * source + 0 * destination. But since the // previous color also needs to be loaded if any original components need // to be kept, force the blend control to something with blending in this // case in temp.z. // temp.z = blending mode used to check if need to load. system_constants_used_ |= 1ull << kSysConst_EdramRTBlendFactorsOps_Index; DxbcOpMovC(temp_z_dest, temp_z_src, DxbcSrc::LU(0), rt_blend_factors_ops_src); // Get if the blend control register requires loading the color to temp.z. // temp.z = whether need to load the color. DxbcOpINE(temp_z_dest, temp_z_src, DxbcSrc::LU(0x00010001)); // Check if need to do something with the previous color. // temp.z = free. DxbcOpIf(true, temp_z_src); { // ********************************************************************* // Loading the previous color to temp.zw. // ********************************************************************* // Get if the format is 64bpp to temp.z. // temp.z = whether the render target is 64bpp. system_constants_used_ |= 1ull << kSysConst_EdramRTFormatFlags_Index; DxbcOpAnd(temp_z_dest, rt_format_flags_src, DxbcSrc::LU(kRTFormatFlag_64bpp)); // Check if the format is 64bpp. // temp.z = free. DxbcOpIf(true, temp_z_src); { // Load the lower 32 bits of the 64bpp color to temp.z. // temp.z = lower 32 bits of the packed color. if (uav_index_edram_ == kBindingIndexUnallocated) { uav_index_edram_ = uav_count_++; } DxbcOpLdUAVTyped( temp_z_dest, DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kWWWW), 1, DxbcSrc::U(uav_index_edram_, uint32_t(UAVRegister::kEdram), DxbcSrc::kXXXX)); // Get the address of the upper 32 bits of the color to temp.w. // temp.w = address of the upper 32 bits of the packed color. DxbcOpIAdd(temp_w_dest, DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kWWWW), DxbcSrc::LU(1)); // Load the upper 32 bits of the 64bpp color to temp.w. // temp.zw = packed destination color/alpha. if (uav_index_edram_ == kBindingIndexUnallocated) { uav_index_edram_ = uav_count_++; } DxbcOpLdUAVTyped( temp_w_dest, temp_w_src, 1, DxbcSrc::U(uav_index_edram_, uint32_t(UAVRegister::kEdram), DxbcSrc::kXXXX)); } // The color is 32bpp. DxbcOpElse(); { // Load the 32bpp color to temp.z. // temp.z = packed 32bpp destination color. if (uav_index_edram_ == kBindingIndexUnallocated) { uav_index_edram_ = uav_count_++; } DxbcOpLdUAVTyped( temp_z_dest, DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kZZZZ), 1, DxbcSrc::U(uav_index_edram_, uint32_t(UAVRegister::kEdram), DxbcSrc::kXXXX)); // Break register dependency in temp.w if the color is 32bpp. // temp.zw = packed destination color/alpha. DxbcOpMov(temp_w_dest, DxbcSrc::LU(0)); } // Close the color format check. DxbcOpEndIf(); uint32_t color_temp = PushSystemTemp(); DxbcDest color_temp_rgb_dest(DxbcDest::R(color_temp, 0b0111)); DxbcDest color_temp_a_dest(DxbcDest::R(color_temp, 0b1000)); DxbcSrc color_temp_src(DxbcSrc::R(color_temp)); DxbcSrc color_temp_a_src(DxbcSrc::R(color_temp, DxbcSrc::kWWWW)); // Get if blending is enabled to color_temp.x. // color_temp.x = whether blending is enabled. system_constants_used_ |= 1ull << kSysConst_EdramRTBlendFactorsOps_Index; DxbcOpINE(DxbcDest::R(color_temp, 0b0001), rt_blend_factors_ops_src, DxbcSrc::LU(0x00010001)); // Check if need to blend. // color_temp.x = free. DxbcOpIf(true, DxbcSrc::R(color_temp, DxbcSrc::kXXXX)); { // Now, when blending is enabled, temp.xy are used as scratch since // the color is packed after blending. // Unpack the destination color to color_temp, using temp.xy as temps. // The destination color never needs clamping because out-of-range // values can't be loaded. // color_temp.xyzw = destination color/alpha. ROV_UnpackColor(i, temp, 2, color_temp, temp, 0, temp, 1); // ******************************************************************* // Color blending. // ******************************************************************* // Extract the color min/max bit to temp.x. // temp.x = whether min/max should be used for color. system_constants_used_ |= 1ull << kSysConst_EdramRTBlendFactorsOps_Index; DxbcOpAnd(temp_x_dest, rt_blend_factors_ops_src, DxbcSrc::LU(1 << (5 + 1))); // Check if need to do blend the color with factors. // temp.x = free. DxbcOpIf(false, temp_x_src); { uint32_t blend_src_temp = PushSystemTemp(); DxbcDest blend_src_temp_rgb_dest( DxbcDest::R(blend_src_temp, 0b0111)); DxbcSrc blend_src_temp_src(DxbcSrc::R(blend_src_temp)); // Extract the source color factor to temp.x. // temp.x = source color factor index. system_constants_used_ |= 1ull << kSysConst_EdramRTBlendFactorsOps_Index; DxbcOpAnd(temp_x_dest, rt_blend_factors_ops_src, DxbcSrc::LU((1 << 5) - 1)); // Check if the source color factor is not zero - if it is, the // source must be ignored completely, and Infinity and NaN in it // shouldn't affect blending. DxbcOpIf(true, temp_x_src); { // Open the switch for choosing the source color blend factor. // temp.x = free. DxbcOpSwitch(temp_x_src); // Write the source color factor to blend_src_temp.xyz. // blend_src_temp.xyz = unclamped source color factor. ROV_HandleColorBlendFactorCases(system_temps_color_[i], color_temp, blend_src_temp); // Close the source color factor switch. DxbcOpEndSwitch(); // Get if the render target color is fixed-point and the source // color factor needs clamping to temp.x. // temp.x = whether color is fixed-point. system_constants_used_ |= 1ull << kSysConst_EdramRTFormatFlags_Index; DxbcOpAnd(temp_x_dest, rt_format_flags_src, DxbcSrc::LU(kRTFormatFlag_FixedPointColor)); // Check if the source color factor needs clamping. DxbcOpIf(true, temp_x_src); { // Clamp the source color factor in blend_src_temp.xyz. // blend_src_temp.xyz = source color factor. system_constants_used_ |= 1ull << kSysConst_EdramRTClamp_Index; DxbcOpMax(blend_src_temp_rgb_dest, blend_src_temp_src, rt_clamp_vec_src.Select(0)); DxbcOpMin(blend_src_temp_rgb_dest, blend_src_temp_src, rt_clamp_vec_src.Select(2)); } // Close the source color factor clamping check. DxbcOpEndIf(); // Apply the factor to the source color. // blend_src_temp.xyz = unclamped source color part without // addition sign. DxbcOpMul(blend_src_temp_rgb_dest, DxbcSrc::R(system_temps_color_[i]), blend_src_temp_src); // Check if the source color part needs clamping after the // multiplication. // temp.x = free. DxbcOpIf(true, temp_x_src); { // Clamp the source color part. // blend_src_temp.xyz = source color part without addition sign. system_constants_used_ |= 1ull << kSysConst_EdramRTClamp_Index; DxbcOpMax(blend_src_temp_rgb_dest, blend_src_temp_src, rt_clamp_vec_src.Select(0)); DxbcOpMin(blend_src_temp_rgb_dest, blend_src_temp_src, rt_clamp_vec_src.Select(2)); } // Close the source color part clamping check. DxbcOpEndIf(); // Extract the source color sign to temp.x. // temp.x = source color sign as zero for 1 and non-zero for -1. system_constants_used_ |= 1ull << kSysConst_EdramRTBlendFactorsOps_Index; DxbcOpAnd(temp_x_dest, rt_blend_factors_ops_src, DxbcSrc::LU(1 << (5 + 2))); // Apply the source color sign. // blend_src_temp.xyz = source color part. // temp.x = free. DxbcOpMovC(blend_src_temp_rgb_dest, temp_x_src, -blend_src_temp_src, blend_src_temp_src); } // The source color factor is zero. DxbcOpElse(); { // Write zero to the source color part. // blend_src_temp.xyz = source color part. // temp.x = free. DxbcOpMov(blend_src_temp_rgb_dest, DxbcSrc::LF(0.0f)); } // Close the source color factor zero check. DxbcOpEndIf(); // Extract the destination color factor to temp.x. // temp.x = destination color factor index. system_constants_used_ |= 1ull << kSysConst_EdramRTBlendFactorsOps_Index; DxbcOpUBFE(temp_x_dest, DxbcSrc::LU(5), DxbcSrc::LU(8), rt_blend_factors_ops_src); // Check if the destination color factor is not zero. DxbcOpIf(true, temp_x_src); { uint32_t blend_dest_factor_temp = PushSystemTemp(); DxbcSrc blend_dest_factor_temp_src( DxbcSrc::R(blend_dest_factor_temp)); // Open the switch for choosing the destination color blend // factor. // temp.x = free. DxbcOpSwitch(temp_x_src); // Write the destination color factor to // blend_dest_factor_temp.xyz. // blend_dest_factor_temp.xyz = unclamped destination color // factor. ROV_HandleColorBlendFactorCases( system_temps_color_[i], color_temp, blend_dest_factor_temp); // Close the destination color factor switch. DxbcOpEndSwitch(); // Get if the render target color is fixed-point and the // destination color factor needs clamping to temp.x. // temp.x = whether color is fixed-point. system_constants_used_ |= 1ull << kSysConst_EdramRTFormatFlags_Index; DxbcOpAnd(temp_x_dest, rt_format_flags_src, DxbcSrc::LU(kRTFormatFlag_FixedPointColor)); // Check if the destination color factor needs clamping. DxbcOpIf(true, temp_x_src); { // Clamp the destination color factor in // blend_dest_factor_temp.xyz. // blend_dest_factor_temp.xyz = destination color factor. system_constants_used_ |= 1ull << kSysConst_EdramRTClamp_Index; DxbcOpMax(DxbcDest::R(blend_dest_factor_temp, 0b0111), blend_dest_factor_temp_src, rt_clamp_vec_src.Select(0)); DxbcOpMin(DxbcDest::R(blend_dest_factor_temp, 0b0111), blend_dest_factor_temp_src, rt_clamp_vec_src.Select(2)); } // Close the destination color factor clamping check. DxbcOpEndIf(); // Apply the factor to the destination color in color_temp.xyz. // color_temp.xyz = unclamped destination color part without // addition sign. // blend_dest_temp.xyz = free. DxbcOpMul(color_temp_rgb_dest, color_temp_src, blend_dest_factor_temp_src); // Release blend_dest_factor_temp. PopSystemTemp(); // Check if the destination color part needs clamping after the // multiplication. // temp.x = free. DxbcOpIf(true, temp_x_src); { // Clamp the destination color part. // color_temp.xyz = destination color part without addition // sign. system_constants_used_ |= 1ull << kSysConst_EdramRTClamp_Index; DxbcOpMax(color_temp_rgb_dest, color_temp_src, rt_clamp_vec_src.Select(0)); DxbcOpMin(color_temp_rgb_dest, color_temp_src, rt_clamp_vec_src.Select(2)); } // Close the destination color part clamping check. DxbcOpEndIf(); // Extract the destination color sign to temp.x. // temp.x = destination color sign as zero for 1 and non-zero for // -1. system_constants_used_ |= 1ull << kSysConst_EdramRTBlendFactorsOps_Index; DxbcOpAnd(temp_x_dest, rt_blend_factors_ops_src, DxbcSrc::LU(1 << 5)); // Select the sign for destination multiply-add as 1.0 or -1.0 to // temp.x. // temp.x = destination color sign as float. DxbcOpMovC(temp_x_dest, temp_x_src, DxbcSrc::LF(-1.0f), DxbcSrc::LF(1.0f)); // Perform color blending to color_temp.xyz. // color_temp.xyz = unclamped blended color. // blend_src_temp.xyz = free. // temp.x = free. DxbcOpMAd(color_temp_rgb_dest, color_temp_src, temp_x_src, blend_src_temp_src); } // The destination color factor is zero. DxbcOpElse(); { // Write the source color part without applying the destination // color. // color_temp.xyz = unclamped blended color. // blend_src_temp.xyz = free. // temp.x = free. DxbcOpMov(color_temp_rgb_dest, blend_src_temp_src); } // Close the destination color factor zero check. DxbcOpEndIf(); // Release blend_src_temp. PopSystemTemp(); // Clamp the color in color_temp.xyz before packing. // color_temp.xyz = blended color. system_constants_used_ |= 1ull << kSysConst_EdramRTClamp_Index; DxbcOpMax(color_temp_rgb_dest, color_temp_src, rt_clamp_vec_src.Select(0)); DxbcOpMin(color_temp_rgb_dest, color_temp_src, rt_clamp_vec_src.Select(2)); } // Need to do min/max for color. DxbcOpElse(); { // Extract the color min (0) or max (1) bit to temp.x // temp.x = whether min or max should be used for color. system_constants_used_ |= 1ull << kSysConst_EdramRTBlendFactorsOps_Index; DxbcOpAnd(temp_x_dest, rt_blend_factors_ops_src, DxbcSrc::LU(1 << 5)); // Check if need to do min or max for color. // temp.x = free. DxbcOpIf(true, temp_x_src); { // Choose max of the colors without applying the factors to // color_temp.xyz. // color_temp.xyz = blended color. DxbcOpMax(color_temp_rgb_dest, DxbcSrc::R(system_temps_color_[i]), color_temp_src); } // Need to do min. DxbcOpElse(); { // Choose min of the colors without applying the factors to // color_temp.xyz. // color_temp.xyz = blended color. DxbcOpMin(color_temp_rgb_dest, DxbcSrc::R(system_temps_color_[i]), color_temp_src); } // Close the min or max check. DxbcOpEndIf(); } // Close the color factor blending or min/max check. DxbcOpEndIf(); // ******************************************************************* // Alpha blending. // ******************************************************************* // Extract the alpha min/max bit to temp.x. // temp.x = whether min/max should be used for alpha. system_constants_used_ |= 1ull << kSysConst_EdramRTBlendFactorsOps_Index; DxbcOpAnd(temp_x_dest, rt_blend_factors_ops_src, DxbcSrc::LU(1 << (21 + 1))); // Check if need to do blend the color with factors. // temp.x = free. DxbcOpIf(false, temp_x_src); { // Extract the source alpha factor to temp.x. // temp.x = source alpha factor index. system_constants_used_ |= 1ull << kSysConst_EdramRTBlendFactorsOps_Index; DxbcOpUBFE(temp_x_dest, DxbcSrc::LU(5), DxbcSrc::LU(16), rt_blend_factors_ops_src); // Check if the source alpha factor is not zero. DxbcOpIf(true, temp_x_src); { // Open the switch for choosing the source alpha blend factor. // temp.x = free. DxbcOpSwitch(temp_x_src); // Write the source alpha factor to temp.x. // temp.x = unclamped source alpha factor. ROV_HandleAlphaBlendFactorCases(system_temps_color_[i], color_temp, temp, 0); // Close the source alpha factor switch. DxbcOpEndSwitch(); // Get if the render target alpha is fixed-point and the source // alpha factor needs clamping to temp.y. // temp.y = whether alpha is fixed-point. system_constants_used_ |= 1ull << kSysConst_EdramRTFormatFlags_Index; DxbcOpAnd(temp_y_dest, rt_format_flags_src, DxbcSrc::LU(kRTFormatFlag_FixedPointAlpha)); // Check if the source alpha factor needs clamping. DxbcOpIf(true, temp_y_src); { // Clamp the source alpha factor in temp.x. // temp.x = source alpha factor. system_constants_used_ |= 1ull << kSysConst_EdramRTClamp_Index; DxbcOpMax(temp_x_dest, temp_x_src, rt_clamp_vec_src.Select(1)); DxbcOpMin(temp_x_dest, temp_x_src, rt_clamp_vec_src.Select(3)); } // Close the source alpha factor clamping check. DxbcOpEndIf(); // Apply the factor to the source alpha. // temp.x = unclamped source alpha part without addition sign. DxbcOpMul(temp_x_dest, DxbcSrc::R(system_temps_color_[i], DxbcSrc::kWWWW), temp_x_src); // Check if the source alpha part needs clamping after the // multiplication. // temp.y = free. DxbcOpIf(true, temp_y_src); { // Clamp the source alpha part. // temp.x = source alpha part without addition sign. system_constants_used_ |= 1ull << kSysConst_EdramRTClamp_Index; DxbcOpMax(temp_x_dest, temp_x_src, rt_clamp_vec_src.Select(1)); DxbcOpMin(temp_x_dest, temp_x_src, rt_clamp_vec_src.Select(3)); } // Close the source alpha part clamping check. DxbcOpEndIf(); // Extract the source alpha sign to temp.y. // temp.y = source alpha sign as zero for 1 and non-zero for -1. system_constants_used_ |= 1ull << kSysConst_EdramRTBlendFactorsOps_Index; DxbcOpAnd(temp_y_dest, rt_blend_factors_ops_src, DxbcSrc::LU(1 << (21 + 2))); // Apply the source alpha sign. // temp.x = source alpha part. DxbcOpMovC(temp_x_dest, temp_y_src, -temp_x_src, temp_x_src); } // The source alpha factor is zero. DxbcOpElse(); { // Write zero to the source alpha part. // temp.x = source alpha part. DxbcOpMov(temp_x_dest, DxbcSrc::LF(0.0f)); } // Close the source alpha factor zero check. DxbcOpEndIf(); // Extract the destination alpha factor to temp.y. // temp.y = destination alpha factor index. system_constants_used_ |= 1ull << kSysConst_EdramRTBlendFactorsOps_Index; DxbcOpUBFE(temp_y_dest, DxbcSrc::LU(5), DxbcSrc::LU(24), rt_blend_factors_ops_src); // Check if the destination alpha factor is not zero. DxbcOpIf(true, temp_y_src); { // Open the switch for choosing the destination alpha blend // factor. // temp.y = free. DxbcOpSwitch(temp_y_src); // Write the destination alpha factor to temp.y. // temp.y = unclamped destination alpha factor. ROV_HandleAlphaBlendFactorCases(system_temps_color_[i], color_temp, temp, 1); // Close the destination alpha factor switch. DxbcOpEndSwitch(); // Get if the render target alpha is fixed-point and the // destination alpha factor needs clamping. // alpha_is_fixed_temp.x = whether alpha is fixed-point. uint32_t alpha_is_fixed_temp = PushSystemTemp(); system_constants_used_ |= 1ull << kSysConst_EdramRTFormatFlags_Index; DxbcOpAnd(DxbcDest::R(alpha_is_fixed_temp, 0b0001), rt_format_flags_src, DxbcSrc::LU(kRTFormatFlag_FixedPointAlpha)); // Check if the destination alpha factor needs clamping. DxbcOpIf(true, DxbcSrc::R(alpha_is_fixed_temp, DxbcSrc::kXXXX)); { // Clamp the destination alpha factor in temp.y. // temp.y = destination alpha factor. system_constants_used_ |= 1ull << kSysConst_EdramRTClamp_Index; DxbcOpMax(temp_y_dest, temp_y_src, rt_clamp_vec_src.Select(1)); DxbcOpMin(temp_y_dest, temp_y_src, rt_clamp_vec_src.Select(3)); } // Close the destination alpha factor clamping check. DxbcOpEndIf(); // Apply the factor to the destination alpha in color_temp.w. // color_temp.w = unclamped destination alpha part without // addition sign. DxbcOpMul(color_temp_a_dest, color_temp_a_src, temp_y_src); // Check if the destination alpha part needs clamping after the // multiplication. // alpha_is_fixed_temp.x = free. DxbcOpIf(true, DxbcSrc::R(alpha_is_fixed_temp, DxbcSrc::kXXXX)); // Release alpha_is_fixed_temp. PopSystemTemp(); { // Clamp the destination alpha part. // color_temp.w = destination alpha part without addition sign. system_constants_used_ |= 1ull << kSysConst_EdramRTClamp_Index; DxbcOpMax(color_temp_a_dest, color_temp_a_src, rt_clamp_vec_src.Select(1)); DxbcOpMin(color_temp_a_dest, color_temp_a_src, rt_clamp_vec_src.Select(3)); } // Close the destination alpha factor clamping check. DxbcOpEndIf(); // Extract the destination alpha sign to temp.y. // temp.y = destination alpha sign as zero for 1 and non-zero for // -1. system_constants_used_ |= 1ull << kSysConst_EdramRTBlendFactorsOps_Index; DxbcOpAnd(temp_y_dest, rt_blend_factors_ops_src, DxbcSrc::LU(1 << 21)); // Select the sign for destination multiply-add as 1.0 or -1.0 to // temp.y. // temp.y = destination alpha sign as float. DxbcOpMovC(temp_y_dest, temp_y_src, DxbcSrc::LF(-1.0f), DxbcSrc::LF(1.0f)); // Perform alpha blending to color_temp.w. // color_temp.w = unclamped blended alpha. // temp.xy = free. DxbcOpMAd(color_temp_a_dest, color_temp_a_src, temp_y_src, temp_x_src); } // The destination alpha factor is zero. DxbcOpElse(); { // Write the source alpha part without applying the destination // alpha. // color_temp.w = unclamped blended alpha. // temp.xy = free. DxbcOpMov(color_temp_a_dest, temp_x_src); } // Close the destination alpha factor zero check. DxbcOpEndIf(); // Clamp the alpha in color_temp.w before packing. // color_temp.w = blended alpha. system_constants_used_ |= 1ull << kSysConst_EdramRTClamp_Index; DxbcOpMax(color_temp_a_dest, color_temp_a_src, rt_clamp_vec_src.Select(1)); DxbcOpMin(color_temp_a_dest, color_temp_a_src, rt_clamp_vec_src.Select(3)); } // Need to do min/max for alpha. DxbcOpElse(); { // Extract the alpha min (0) or max (1) bit to temp.x. // temp.x = whether min or max should be used for alpha. system_constants_used_ |= 1ull << kSysConst_EdramRTBlendFactorsOps_Index; DxbcOpAnd(temp_x_dest, rt_blend_factors_ops_src, DxbcSrc::LU(1 << 21)); // Check if need to do min or max for alpha. // temp.x = free. DxbcOpIf(true, temp_x_src); { // Choose max of the alphas without applying the factors to // color_temp.w. // color_temp.w = blended alpha. DxbcOpMax(color_temp_a_dest, DxbcSrc::R(system_temps_color_[i], DxbcSrc::kWWWW), color_temp_a_src); } // Need to do min. DxbcOpElse(); { // Choose min of the alphas without applying the factors to // color_temp.w. // color_temp.w = blended alpha. DxbcOpMin(color_temp_a_dest, DxbcSrc::R(system_temps_color_[i], DxbcSrc::kWWWW), color_temp_a_src); } // Close the min or max check. DxbcOpEndIf(); } // Close the alpha factor blending or min/max check. DxbcOpEndIf(); // Pack the new color/alpha to temp.xy. // temp.xy = packed new color/alpha. uint32_t color_pack_temp = PushSystemTemp(); ROV_PackPreClampedColor(i, color_temp, temp, 0, color_pack_temp, 0, color_pack_temp, 1); // Release color_pack_temp. PopSystemTemp(); } // Close the blending check. DxbcOpEndIf(); // ********************************************************************* // Write mask application // ********************************************************************* // Apply the keep mask to the previous packed color/alpha in temp.zw. // temp.zw = masked packed old color/alpha. system_constants_used_ |= 1ull << kSysConst_EdramRTKeepMask_Index; DxbcOpAnd(DxbcDest::R(temp, 0b1100), DxbcSrc::R(temp), keep_mask_vec_src.Swizzle(keep_mask_swizzle << 4)); // Invert the keep mask into color_temp.xy. // color_temp.xy = inverted keep mask (write mask). system_constants_used_ |= 1ull << kSysConst_EdramRTKeepMask_Index; DxbcOpNot(DxbcDest::R(color_temp, 0b0011), keep_mask_vec_src.Swizzle(keep_mask_swizzle)); // Release color_temp. PopSystemTemp(); // Apply the write mask to the new color/alpha in temp.xy. // temp.xy = masked packed new color/alpha. DxbcOpAnd(DxbcDest::R(temp, 0b0011), DxbcSrc::R(temp), DxbcSrc::R(color_temp)); // Combine the masked colors into temp.xy. // temp.xy = packed resulting color/alpha. // temp.zw = free. DxbcOpOr(DxbcDest::R(temp, 0b0011), DxbcSrc::R(temp), DxbcSrc::R(temp, 0b1110)); } // Close the previous color load check. DxbcOpEndIf(); // *********************************************************************** // Writing the color // *********************************************************************** // Get if the format is 64bpp to temp.z. // temp.z = whether the render target is 64bpp. system_constants_used_ |= 1ull << kSysConst_EdramRTFormatFlags_Index; DxbcOpAnd(temp_z_dest, rt_format_flags_src, DxbcSrc::LU(kRTFormatFlag_64bpp)); // Check if the format is 64bpp. // temp.z = free. DxbcOpIf(true, temp_z_src); { // Store the lower 32 bits of the 64bpp color. if (uav_index_edram_ == kBindingIndexUnallocated) { uav_index_edram_ = uav_count_++; } DxbcOpStoreUAVTyped( DxbcDest::U(uav_index_edram_, uint32_t(UAVRegister::kEdram)), DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kWWWW), 1, temp_x_src); // Get the address of the upper 32 bits of the color to temp.z (can't // use temp.x because components when not blending, packing is done once // for all samples, so it has to be preserved). DxbcOpIAdd(temp_z_dest, DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kWWWW), DxbcSrc::LU(1)); // Store the upper 32 bits of the 64bpp color. if (uav_index_edram_ == kBindingIndexUnallocated) { uav_index_edram_ = uav_count_++; } DxbcOpStoreUAVTyped( DxbcDest::U(uav_index_edram_, uint32_t(UAVRegister::kEdram)), temp_z_src, 1, temp_y_src); } // The color is 32bpp. DxbcOpElse(); { // Store the 32bpp color. if (uav_index_edram_ == kBindingIndexUnallocated) { uav_index_edram_ = uav_count_++; } DxbcOpStoreUAVTyped( DxbcDest::U(uav_index_edram_, uint32_t(UAVRegister::kEdram)), DxbcSrc::R(system_temp_rov_params_, DxbcSrc::kZZZZ), 1, temp_x_src); } // Close the 64bpp/32bpp conditional. DxbcOpEndIf(); // *********************************************************************** // End of color sample raster operation. // *********************************************************************** // Close the sample covered check. DxbcOpEndIf(); // Go to the next sample (samples are at +0, +80, +1, +81, so need to do // +80, -79, +80 and -81 after each sample). system_constants_used_ |= 1ull << kSysConst_EdramResolutionSquareScale_Index; DxbcOpIMAd(DxbcDest::R(system_temp_rov_params_, 0b1100), DxbcSrc::LI(0, 0, (j & 1) ? -78 - j : 80, ((j & 1) ? -78 - j : 80) * 2), DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_EdramResolutionSquareScale_Vec) .Select(kSysConst_EdramResolutionSquareScale_Comp), DxbcSrc::R(system_temp_rov_params_)); } // Revert adding the EDRAM bases of the render target to // system_temp_rov_params_.zw. system_constants_used_ |= 1ull << kSysConst_EdramRTBaseDwordsScaled_Index; DxbcOpIAdd(DxbcDest::R(system_temp_rov_params_, 0b1100), DxbcSrc::R(system_temp_rov_params_), -DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_EdramRTBaseDwordsScaled_Vec) .Select(i)); // Close the render target write check. DxbcOpEndIf(); } // Release temp. PopSystemTemp(); } void DxbcShaderTranslator::CompletePixelShader() { if (is_depth_only_pixel_shader_) { // The depth-only shader only needs to do the depth test and to write the // depth to the ROV. if (edram_rov_used_) { CompletePixelShader_WriteToROV(); } return; } if (writes_color_target(0)) { // Alpha test. // X - mask, then masked result (SGPR for loading, VGPR for masking). // Y - operation result (SGPR for mask operations, VGPR for alpha // operations). uint32_t alpha_test_temp = PushSystemTemp(); DxbcDest alpha_test_mask_dest(DxbcDest::R(alpha_test_temp, 0b0001)); DxbcSrc alpha_test_mask_src(DxbcSrc::R(alpha_test_temp, DxbcSrc::kXXXX)); DxbcDest alpha_test_op_dest(DxbcDest::R(alpha_test_temp, 0b0010)); DxbcSrc alpha_test_op_src(DxbcSrc::R(alpha_test_temp, DxbcSrc::kYYYY)); // Extract the comparison mask to check if the test needs to be done at all. // Don't care about flow control being somewhat dynamic - early Z is forced // using a special version of the shader anyway. system_constants_used_ |= 1ull << kSysConst_Flags_Index; DxbcOpUBFE(alpha_test_mask_dest, DxbcSrc::LU(3), DxbcSrc::LU(kSysFlag_AlphaPassIfLess_Shift), DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_Flags_Vec) .Select(kSysConst_Flags_Comp)); // Compare the mask to ALWAYS to check if the test shouldn't be done (will // pass even for NaNs, though the expected behavior in this case hasn't been // checked, but let's assume this means "always", not "less, equal or // greater". // TODO(Triang3l): Check how alpha test works with NaN on Direct3D 9. DxbcOpINE(alpha_test_op_dest, alpha_test_mask_src, DxbcSrc::LU(0b111)); // Don't do the test if the mode is "always". DxbcOpIf(true, alpha_test_op_src); { // Do the test. Can't use subtraction and sign because of float specials. DxbcSrc alpha_src(DxbcSrc::R(system_temps_color_[0], DxbcSrc::kWWWW)); system_constants_used_ |= 1ull << kSysConst_AlphaTestReference_Index; DxbcSrc alpha_test_reference_src( DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_AlphaTestReference_Vec) .Select(kSysConst_AlphaTestReference_Comp)); // Less than. DxbcOpLT(alpha_test_op_dest, alpha_src, alpha_test_reference_src); DxbcOpOr(alpha_test_op_dest, alpha_test_op_src, DxbcSrc::LU(~uint32_t(1 << 0))); DxbcOpAnd(alpha_test_mask_dest, alpha_test_mask_src, alpha_test_op_src); // Equals to. DxbcOpEq(alpha_test_op_dest, alpha_src, alpha_test_reference_src); DxbcOpOr(alpha_test_op_dest, alpha_test_op_src, DxbcSrc::LU(~uint32_t(1 << 1))); DxbcOpAnd(alpha_test_mask_dest, alpha_test_mask_src, alpha_test_op_src); // Greater than. DxbcOpLT(alpha_test_op_dest, alpha_test_reference_src, alpha_src); DxbcOpOr(alpha_test_op_dest, alpha_test_op_src, DxbcSrc::LU(~uint32_t(1 << 2))); DxbcOpAnd(alpha_test_mask_dest, alpha_test_mask_src, alpha_test_op_src); // Discard the pixel if it has failed the test. if (edram_rov_used_) { DxbcOpRetC(false, alpha_test_mask_src); } else { DxbcOpDiscard(false, alpha_test_mask_src); } } // Close the "not always" check. DxbcOpEndIf(); // Release alpha_test_temp. PopSystemTemp(); } // Write the values to the render targets. Not applying the exponent bias yet // because the original 0 to 1 alpha value is needed for alpha to coverage, // which is done differently for ROV and RTV/DSV. if (edram_rov_used_) { CompletePixelShader_WriteToROV(); } else { CompletePixelShader_WriteToRTVs(); } } void DxbcShaderTranslator::ROV_DepthTo24Bit(uint32_t d24_temp, uint32_t d24_temp_component, uint32_t d32_temp, uint32_t d32_temp_component, uint32_t temp_temp, uint32_t temp_temp_component) { assert_true(temp_temp != d24_temp || temp_temp_component != d24_temp_component); assert_true(temp_temp != d32_temp || temp_temp_component != d32_temp_component); // Source and destination may be the same. DxbcDest d24_dest(DxbcDest::R(d24_temp, 1 << d24_temp_component)); DxbcSrc d24_src(DxbcSrc::R(d24_temp).Select(d24_temp_component)); DxbcSrc d32_src(DxbcSrc::R(d32_temp).Select(d32_temp_component)); DxbcDest temp_dest(DxbcDest::R(temp_temp, 1 << temp_temp_component)); DxbcSrc temp_src(DxbcSrc::R(temp_temp).Select(temp_temp_component)); system_constants_used_ |= 1ull << kSysConst_Flags_Index; DxbcOpAnd(temp_dest, DxbcSrc::CB(cbuffer_index_system_constants_, uint32_t(CbufferRegister::kSystemConstants), kSysConst_Flags_Vec) .Select(kSysConst_Flags_Comp), DxbcSrc::LU(kSysFlag_ROVDepthFloat24)); // Convert according to the format. DxbcOpIf(true, temp_src); { // 20e4 conversion, using 1 VGPR. // CFloat24 from d3dref9.dll. // Assuming the depth is already clamped to [0, 2) (in all places, the depth // is written with the saturate flag set). // Check if the number is too small to be represented as normalized 20e4. // temp = f32 < 2^-14 DxbcOpULT(temp_dest, d32_src, DxbcSrc::LU(0x38800000)); // Handle denormalized numbers separately. DxbcOpIf(true, temp_src); { // temp = f32 >> 23 DxbcOpUShR(temp_dest, d32_src, DxbcSrc::LU(23)); // temp = 113 - (f32 >> 23) DxbcOpIAdd(temp_dest, DxbcSrc::LI(113), -temp_src); // Don't allow the shift to overflow, since in DXBC the lower 5 bits of // the shift amount are used (otherwise 0 becomes 8). // temp = min(113 - (f32 >> 23), 24) DxbcOpUMin(temp_dest, temp_src, DxbcSrc::LU(24)); // biased_f32 = (f32 & 0x7FFFFF) | 0x800000 DxbcOpBFI(d24_dest, DxbcSrc::LU(9), DxbcSrc::LU(23), DxbcSrc::LU(1), d32_src); // biased_f32 = // ((f32 & 0x7FFFFF) | 0x800000) >> min(113 - (f32 >> 23), 24) DxbcOpUShR(d24_dest, d24_src, temp_src); } // Not denormalized? DxbcOpElse(); { // Bias the exponent. // biased_f32 = f32 + (-112 << 23) // (left shift of a negative value is undefined behavior) DxbcOpIAdd(d24_dest, d32_src, DxbcSrc::LU(0xC8000000u)); } // Close the denormal check. DxbcOpEndIf(); // Build the 20e4 number. // temp = (biased_f32 >> 3) & 1 DxbcOpUBFE(temp_dest, DxbcSrc::LU(1), DxbcSrc::LU(3), d24_src); // f24 = biased_f32 + 3 DxbcOpIAdd(d24_dest, d24_src, DxbcSrc::LU(3)); // f24 = biased_f32 + 3 + ((biased_f32 >> 3) & 1) DxbcOpIAdd(d24_dest, d24_src, temp_src); // f24 = ((biased_f32 + 3 + ((biased_f32 >> 3) & 1)) >> 3) & 0xFFFFFF DxbcOpUBFE(d24_dest, DxbcSrc::LU(24), DxbcSrc::LU(3), d24_src); } DxbcOpElse(); { // Unorm24 conversion. // Multiply by float(0xFFFFFF). DxbcOpMul(d24_dest, d32_src, DxbcSrc::LF(16777215.0f)); // Round to the nearest even integer. This seems to be the correct way: // rounding towards zero gives 0xFF instead of 0x100 in clear shaders in, // for instance, Halo 3, but other clear shaders in it are also broken if // 0.5 is added before ftou instead of round_ne. DxbcOpRoundNE(d24_dest, d24_src); // Convert to fixed-point. DxbcOpFToU(d24_dest, d24_src); } DxbcOpEndIf(); } } // namespace gpu } // namespace xe