/** ****************************************************************************** * 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 #include #include #include #include #include "third_party/dxbc/DXBCChecksum.h" #include "third_party/dxbc/d3d12TokenizedProgramFormat.hpp" #include "xenia/base/assert.h" #include "xenia/base/math.h" #include "xenia/base/string.h" DEFINE_bool(dxbc_indexable_temps, true, "Use indexable temporary registers in translated DXBC shaders for " "relative addressing of general-purpose registers - shaders rarely " "do that, but when they do, this may improve performance on AMD, " "but may cause unknown issues on Nvidia."); DEFINE_bool(dxbc_switch, true, "Use switch rather than if for flow control. Turning this off or " "on may improve stability, though this heavily depends on the " "driver - on AMD, it's recommended to have this set to true, as " "Halo 3 appears to crash when if is used for flow control " "(possibly the shader compiler tries to flatten them)."); DEFINE_bool(dxbc_source_map, false, "Disassemble Xenos instructions as comments in the resulting DXBC " "for debugging."); namespace xe { namespace gpu { using namespace ucode; // Notes about operands: // // Reading and writing: // - Writes to 4-component registers must be masked. // - Reads from 4-component registers can be swizzled, or 1 component can be // selected. // - r# (temporary registers) are 4-component and can be used anywhere. // - v# (inputs) are 4-component and read-only. // - o# (outputs) are 4-component and write-only. // - oDepth (pixel shader depth output) is 1-component and write-only. // - x# (indexable temporary registers) are 4-component (though not sure what // happens if you dcl them as 1-component) and can be accessed either via // a mov load or a mov store (and those movs are counted as ArrayInstructions // in STAT, not as MovInstructions). // // Indexing: // - Constant buffers use 3D indices in CBx[y][z] format, where x is the ID of // the binding (CB#), y is the register to access within its space, z is the // 4-component vector to access within the register binding. // For example, if the requested vector is located in the beginning of the // second buffer in the descriptor array at b2, which is assigned to CB1, the // index would be CB1[3][0]. // - Resources and samplers use 2D indices, where the first dimension is the // S#/T#/U# binding index, and the second is the s#/t#/u# register index // within its space. constexpr uint32_t DxbcShaderTranslator::kMaxTextureSRVIndexBits; constexpr uint32_t DxbcShaderTranslator::kMaxTextureSRVs; constexpr uint32_t DxbcShaderTranslator::kMaxSamplerBindingIndexBits; constexpr uint32_t DxbcShaderTranslator::kMaxSamplerBindings; constexpr uint32_t DxbcShaderTranslator::kInterpolatorCount; constexpr uint32_t DxbcShaderTranslator::kPointParametersTexCoord; constexpr uint32_t DxbcShaderTranslator::kSwizzleXYZW; constexpr uint32_t DxbcShaderTranslator::kSwizzleXXXX; constexpr uint32_t DxbcShaderTranslator::kSwizzleYYYY; constexpr uint32_t DxbcShaderTranslator::kSwizzleZZZZ; constexpr uint32_t DxbcShaderTranslator::kSwizzleWWWW; constexpr uint32_t DxbcShaderTranslator::DxbcSourceOperand::kIntermediateRegisterNone; constexpr uint32_t DxbcShaderTranslator::kCbufferIndexUnallocated; constexpr uint32_t DxbcShaderTranslator::kCfExecBoolConstantNone; DxbcShaderTranslator::DxbcShaderTranslator(bool edram_rov_used) : edram_rov_used_(edram_rov_used) { // Don't allocate again and again for the first shader. shader_code_.reserve(8192); shader_object_.reserve(16384); float_constant_index_offsets_.reserve(512); } DxbcShaderTranslator::~DxbcShaderTranslator() = default; uint32_t DxbcShaderTranslator::GetColorFormatRTFlags( ColorRenderTargetFormat format) { static const uint32_t kRTFormatFlags[16] = { // k_8_8_8_8 kRTFlag_FormatFixed, // k_8_8_8_8_GAMMA kRTFlag_FormatFixed, // k_2_10_10_10 kRTFlag_FormatFixed, // k_2_10_10_10_FLOAT kRTFlag_FormatFloat10, // k_16_16 kRTFlag_FormatFixed | kRTFlag_FormatUnusedB | kRTFlag_FormatUnusedA, // k_16_16_16_16 kRTFlag_FormatFixed, // k_16_16_FLOAT kRTFlag_FormatFloat16 | kRTFlag_FormatUnusedB | kRTFlag_FormatUnusedA, // k_16_16_16_16_FLOAT kRTFlag_FormatFloat16, // Unused kRTFlag_FormatUnusedR | kRTFlag_FormatUnusedG | kRTFlag_FormatUnusedB | kRTFlag_FormatUnusedA, // Unused kRTFlag_FormatUnusedR | kRTFlag_FormatUnusedG | kRTFlag_FormatUnusedB | kRTFlag_FormatUnusedA, // k_2_10_10_10_AS_16_16_16_16 kRTFlag_FormatFixed, // Unused. kRTFlag_FormatUnusedR | kRTFlag_FormatUnusedG | kRTFlag_FormatUnusedB | kRTFlag_FormatUnusedA, // k_2_10_10_10_FLOAT_AS_16_16_16_16 kRTFlag_FormatFloat10, // Unused. kRTFlag_FormatUnusedR | kRTFlag_FormatUnusedG | kRTFlag_FormatUnusedB | kRTFlag_FormatUnusedA, // k_32_FLOAT kRTFlag_FormatUnusedG | kRTFlag_FormatUnusedB | kRTFlag_FormatUnusedA, // k_32_32_FLOAT kRTFlag_FormatUnusedB | kRTFlag_FormatUnusedA, }; return kRTFormatFlags[uint32_t(format)]; } void DxbcShaderTranslator::SetColorFormatSystemConstants( SystemConstants& constants, uint32_t rt_index, ColorRenderTargetFormat format) { constants.edram_rt_pack_width_high[rt_index] = 0; constants.edram_rt_pack_offset_high[rt_index] = 0; uint32_t color_mask = UINT32_MAX, alpha_mask = UINT32_MAX; uint32_t color_min = 0, alpha_min = 0; uint32_t color_max = 0x3F800000, alpha_max = 0x3F800000; float color_load_scale = 1.0f, alpha_load_scale = 1.0f; float color_store_scale = 1.0f, alpha_store_scale = 1.0f; switch (format) { case ColorRenderTargetFormat::k_8_8_8_8: case ColorRenderTargetFormat::k_8_8_8_8_GAMMA: constants.edram_rt_pack_width_low[rt_index] = 8 | (8 << 8) | (8 << 16) | (8 << 24); constants.edram_rt_pack_offset_low[rt_index] = (8 << 8) | (16 << 16) | (24 << 24); color_mask = alpha_mask = 255; color_load_scale = alpha_load_scale = 1.0f / 255.0f; color_store_scale = alpha_store_scale = 255.0f; break; case ColorRenderTargetFormat::k_2_10_10_10: case ColorRenderTargetFormat::k_2_10_10_10_AS_16_16_16_16: constants.edram_rt_pack_width_low[rt_index] = 10 | (10 << 8) | (10 << 16) | (2 << 24); constants.edram_rt_pack_offset_low[rt_index] = (10 << 8) | (20 << 16) | (30 << 24); color_mask = 1023; alpha_mask = 3; color_load_scale = 1.0f / 1023.0f; alpha_load_scale = 1.0f / 3.0f; color_store_scale = 1023.0f; alpha_store_scale = 3.0f; break; case ColorRenderTargetFormat::k_2_10_10_10_FLOAT: case ColorRenderTargetFormat::k_2_10_10_10_FLOAT_AS_16_16_16_16: constants.edram_rt_pack_width_low[rt_index] = 10 | (10 << 8) | (10 << 16) | (2 << 24); constants.edram_rt_pack_offset_low[rt_index] = (10 << 8) | (20 << 16) | (30 << 24); color_mask = 1023; alpha_mask = 3; // 31.875. color_max = 0x41FF0000; alpha_load_scale = 1.0f / 3.0f; alpha_store_scale = 3.0f; break; case ColorRenderTargetFormat::k_16_16: case ColorRenderTargetFormat::k_16_16_16_16: constants.edram_rt_pack_width_low[rt_index] = 16 | (16 << 8); constants.edram_rt_pack_offset_low[rt_index] = 16 << 8; if (format == ColorRenderTargetFormat::k_16_16_16_16) { constants.edram_rt_pack_width_high[rt_index] = (16 << 16) | (16 << 24); constants.edram_rt_pack_offset_high[rt_index] = 16 << 24; } // -32.0. color_min = alpha_min = 0xC2000000u; // 32.0. color_max = alpha_max = 0x42000000u; color_load_scale = alpha_load_scale = 32.0f / 32767.0f; color_store_scale = alpha_store_scale = 32767.0f / 32.0f; break; case ColorRenderTargetFormat::k_16_16_FLOAT: case ColorRenderTargetFormat::k_16_16_16_16_FLOAT: constants.edram_rt_pack_width_low[rt_index] = 16 | (16 << 8); constants.edram_rt_pack_offset_low[rt_index] = 16 << 8; if (format == ColorRenderTargetFormat::k_16_16_16_16_FLOAT) { constants.edram_rt_pack_width_high[rt_index] = (16 << 16) | (16 << 24); constants.edram_rt_pack_offset_high[rt_index] = 16 << 24; } color_mask = alpha_mask = 0xFFFF; // -65504.0 to 65504.0 - the Xbox 360 doesn't have Infinity or NaN in // float16, instead it has the range expanded to 131008.0, however, // supporting it correctly would require making changes to texture // formats (float32 would be required for emulating textures, which is // pretty big, resolves also will require conversion; vertex fetch, vpkd3d // CPU instruction). The precision in the 65504-131008 range is very low // anyway, let's hope games don't really rely on it. So let's only clamp // to a finite value to remove specials from blending. // https://blogs.msdn.microsoft.com/chuckw/2013/03/05/known-issues-directxmath-3-03/ // TODO(Triang3l): Maybe handle float16 correctly everywhere. color_min = alpha_min = 0xC77FE000u; color_max = alpha_max = 0x477FE000u; break; case ColorRenderTargetFormat::k_32_FLOAT: case ColorRenderTargetFormat::k_32_32_FLOAT: constants.edram_rt_pack_width_low[rt_index] = 32; constants.edram_rt_pack_offset_low[rt_index] = 0; if (format == ColorRenderTargetFormat::k_32_32_FLOAT) { constants.edram_rt_pack_width_high[rt_index] = 32; } // -Infinity. color_min = alpha_min = 0xFF800000u; // Infinity. color_max = alpha_max = 0x7F800000u; break; default: assert_always(); break; } uint32_t rt_pair_index = rt_index >> 1; uint32_t rt_pair_comp = (rt_index & 1) << 1; constants.edram_load_mask_rt01_rt23[rt_pair_index][rt_pair_comp] = color_mask; constants.edram_load_mask_rt01_rt23[rt_pair_index][rt_pair_comp + 1] = alpha_mask; constants.edram_load_scale_rt01_rt23[rt_pair_index][rt_pair_comp] = color_load_scale; constants.edram_load_scale_rt01_rt23[rt_pair_index][rt_pair_comp + 1] = alpha_load_scale; constants.edram_store_min_rt01_rt23[rt_pair_index][rt_pair_comp] = color_min; constants.edram_store_min_rt01_rt23[rt_pair_index][rt_pair_comp + 1] = alpha_min; constants.edram_store_max_rt01_rt23[rt_pair_index][rt_pair_comp] = color_max; constants.edram_store_max_rt01_rt23[rt_pair_index][rt_pair_comp + 1] = alpha_max; constants.edram_store_scale_rt01_rt23[rt_pair_index][rt_pair_comp] = color_store_scale; constants.edram_store_scale_rt01_rt23[rt_pair_index][rt_pair_comp + 1] = alpha_store_scale; } bool DxbcShaderTranslator::GetBlendConstants(uint32_t blend_control, uint32_t& blend_x_out, uint32_t& blend_y_out) { static const uint32_t kBlendXSrcFactorMap[32] = { 0, kBlendX_Src_One, 0, 0, kBlendX_Src_SrcColor_Pos, kBlendX_Src_One | kBlendX_Src_SrcColor_Neg, kBlendX_Src_SrcAlpha_Pos, kBlendX_Src_One | kBlendX_Src_SrcAlpha_Neg, kBlendX_Src_DestColor_Pos, kBlendX_Src_One | kBlendX_Src_DestColor_Neg, kBlendX_Src_DestAlpha_Pos, kBlendX_Src_One | kBlendX_Src_DestAlpha_Neg, 0, kBlendX_Src_One, 0, kBlendX_Src_One, kBlendX_Src_SrcAlphaSaturate, }; static const uint32_t kBlendYSrcFactorMap[32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, kBlendY_Src_ConstantColor_Pos, kBlendY_Src_ConstantColor_Neg, kBlendY_Src_ConstantAlpha_Pos, kBlendY_Src_ConstantAlpha_Neg, 0, }; static const uint32_t kBlendXSrcAlphaFactorMap[32] = { 0, kBlendX_SrcAlpha_One, 0, 0, kBlendX_SrcAlpha_SrcAlpha_Pos, kBlendX_SrcAlpha_One | kBlendX_SrcAlpha_SrcAlpha_Neg, kBlendX_SrcAlpha_SrcAlpha_Pos, kBlendX_SrcAlpha_One | kBlendX_SrcAlpha_SrcAlpha_Neg, kBlendX_SrcAlpha_DestAlpha_Pos, kBlendX_SrcAlpha_One | kBlendX_SrcAlpha_DestAlpha_Neg, kBlendX_SrcAlpha_DestAlpha_Pos, kBlendX_SrcAlpha_One | kBlendX_SrcAlpha_DestAlpha_Neg, 0, kBlendX_SrcAlpha_One, 0, kBlendX_SrcAlpha_One, kBlendX_SrcAlpha_One, }; static const uint32_t kBlendYSrcAlphaFactorMap[32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, kBlendY_SrcAlpha_ConstantAlpha_Pos, kBlendY_SrcAlpha_ConstantAlpha_Neg, kBlendY_SrcAlpha_ConstantAlpha_Pos, kBlendY_SrcAlpha_ConstantAlpha_Neg, 0, }; static const uint32_t kBlendXDestFactorMap[32] = { 0, kBlendX_Dest_One, 0, 0, kBlendX_Dest_SrcColor_Pos, kBlendX_Dest_One | kBlendX_Dest_SrcColor_Neg, kBlendX_Dest_SrcAlpha_Pos, kBlendX_Dest_One | kBlendX_Dest_SrcAlpha_Neg, kBlendX_Dest_DestColor_Pos, kBlendX_Dest_One | kBlendX_Dest_DestColor_Neg, kBlendX_Dest_DestAlpha_Pos, kBlendX_Dest_One | kBlendX_Dest_DestAlpha_Neg, 0, kBlendX_Dest_One, 0, kBlendX_Dest_One, kBlendX_Dest_SrcAlphaSaturate, }; static const uint32_t kBlendYDestFactorMap[32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, kBlendY_Dest_ConstantColor_Pos, kBlendY_Dest_ConstantColor_Neg, kBlendY_Dest_ConstantAlpha_Pos, kBlendY_Dest_ConstantAlpha_Neg, 0, }; static const uint32_t kBlendXDestAlphaFactorMap[32] = { 0, kBlendX_DestAlpha_One, 0, 0, kBlendX_DestAlpha_SrcAlpha_Pos, kBlendX_DestAlpha_One | kBlendX_DestAlpha_SrcAlpha_Neg, kBlendX_DestAlpha_SrcAlpha_Pos, kBlendX_DestAlpha_One | kBlendX_DestAlpha_SrcAlpha_Neg, kBlendX_DestAlpha_DestAlpha_Pos, kBlendX_DestAlpha_One | kBlendX_DestAlpha_DestAlpha_Neg, kBlendX_DestAlpha_DestAlpha_Pos, kBlendX_DestAlpha_One | kBlendX_DestAlpha_DestAlpha_Neg, 0, kBlendX_DestAlpha_One, 0, kBlendX_DestAlpha_One, kBlendX_DestAlpha_One, }; static const uint32_t kBlendYDestAlphaFactorMap[32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, kBlendY_DestAlpha_ConstantAlpha_Pos, kBlendY_DestAlpha_ConstantAlpha_Neg, kBlendY_DestAlpha_ConstantAlpha_Pos, kBlendY_DestAlpha_ConstantAlpha_Neg, 0, }; uint32_t blend_x = 0, blend_y = 0; // Min and max don't use the factors. BlendOp op_color = BlendOp((blend_control >> 5) & 0x7); if (op_color == BlendOp::kMin) { blend_y |= kBlendY_Color_OpMin; } else if (op_color == BlendOp::kMax) { blend_y |= kBlendY_Color_OpMax; } else { uint32_t src_factor = blend_control & 0x1F; uint32_t dest_factor = (blend_control >> 8) & 0x1F; blend_x |= kBlendXSrcFactorMap[src_factor] | kBlendXDestFactorMap[dest_factor]; blend_y |= kBlendYSrcFactorMap[src_factor] | kBlendYDestFactorMap[dest_factor]; switch (op_color) { case BlendOp::kAdd: blend_y |= kBlendY_Src_OpSign_Pos | kBlendY_Dest_OpSign_Pos; break; case BlendOp::kSubtract: blend_y |= kBlendY_Src_OpSign_Pos | kBlendY_Dest_OpSign_Neg; break; case BlendOp::kRevSubtract: blend_y |= kBlendY_Src_OpSign_Neg | kBlendY_Dest_OpSign_Pos; break; default: assert_always(); } } BlendOp op_alpha = BlendOp((blend_control >> 21) & 0x7); if (op_alpha == BlendOp::kMin) { blend_y |= kBlendY_Alpha_OpMin; } else if (op_alpha == BlendOp::kMax) { blend_y |= kBlendY_Alpha_OpMax; } else { uint32_t src_alpha_factor = (blend_control >> 16) & 0x1F; uint32_t dest_alpha_factor = (blend_control >> 24) & 0x1F; blend_x |= kBlendXSrcAlphaFactorMap[src_alpha_factor] | kBlendXDestAlphaFactorMap[dest_alpha_factor]; blend_y |= kBlendYSrcAlphaFactorMap[src_alpha_factor] | kBlendYDestAlphaFactorMap[dest_alpha_factor]; switch (op_alpha) { case BlendOp::kAdd: blend_y |= kBlendY_SrcAlpha_OpSign_Pos | kBlendY_DestAlpha_OpSign_Pos; break; case BlendOp::kSubtract: blend_y |= kBlendY_SrcAlpha_OpSign_Pos | kBlendY_DestAlpha_OpSign_Neg; break; case BlendOp::kRevSubtract: blend_y |= kBlendY_SrcAlpha_OpSign_Neg | kBlendY_DestAlpha_OpSign_Pos; break; default: assert_always(); } } blend_x_out = blend_x; blend_y_out = blend_y; // 1 * src + 0 * dest is nop, don't waste GPU time. return (blend_control & 0x1FFF1FFF) != 0x00010001; } std::vector DxbcShaderTranslator::CreateDepthOnlyPixelShader() { Reset(); is_depth_only_pixel_shader_ = true; StartTranslation(); return std::move(CompleteTranslation()); } void DxbcShaderTranslator::Reset() { ShaderTranslator::Reset(); shader_code_.clear(); is_depth_only_pixel_shader_ = false; cbuffer_count_ = 0; // System constants always used in prologues/epilogues. cbuffer_index_system_constants_ = cbuffer_count_++; cbuffer_index_float_constants_ = kCbufferIndexUnallocated; cbuffer_index_bool_loop_constants_ = kCbufferIndexUnallocated; cbuffer_index_fetch_constants_ = kCbufferIndexUnallocated; system_constants_used_ = 0; float_constants_dynamic_indexed_ = false; bool_loop_constants_dynamic_indexed_ = false; float_constant_index_offsets_.clear(); system_temp_count_current_ = 0; system_temp_count_max_ = 0; cf_exec_bool_constant_ = kCfExecBoolConstantNone; cf_exec_predicated_ = false; cf_instruction_predicate_if_open_ = false; cf_exec_predicate_written_ = false; writes_depth_ = false; texture_srvs_.clear(); sampler_bindings_.clear(); std::memset(&stat_, 0, sizeof(stat_)); } uint32_t DxbcShaderTranslator::PushSystemTemp(bool zero) { uint32_t register_index = system_temp_count_current_; if (!IndexableGPRsUsed() && !is_depth_only_pixel_shader_) { // Guest shader registers first if they're not in x0. Depth-only pixel // shader is a special case of the DXBC translator usage, where there are no // GPRs because there's no shader to translate, and a guest shader is not // loaded. register_index += register_count(); } ++system_temp_count_current_; system_temp_count_max_ = std::max(system_temp_count_max_, system_temp_count_current_); if (zero) { shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(8)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(register_index); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.mov_instruction_count; } return register_index; } void DxbcShaderTranslator::PopSystemTemp(uint32_t count) { assert_true(count <= system_temp_count_current_); system_temp_count_current_ -= std::min(count, system_temp_count_current_); } bool DxbcShaderTranslator::IndexableGPRsUsed() const { return FLAGS_dxbc_indexable_temps && uses_register_dynamic_addressing(); } void DxbcShaderTranslator::StartVertexShader_LoadVertexIndex() { // Vertex index is in an input bound to SV_VertexID, byte swapped according to // xe_vertex_index_endian system constant and written to GPR 0 (which is // always present because register_count includes +1). // TODO(Triang3l): Check if there's vs_param_gen. // xe_vertex_index_endian is: // - 00 for no swap. // - 01 for 8-in-16. // - 10 for 8-in-32 (8-in-16 and 16-in-32). // - 11 for 16-in-32. // Write to GPR 0 - either directly if not using indexable registers, or via a // system temporary register. uint32_t reg; if (IndexableGPRsUsed()) { reg = PushSystemTemp(); } else { reg = 0; } // 8-in-16: Create target for A and C insertion in Y and sources in X and Z. // ushr reg.xyz, input, l(0, 8, 16, 0) // ABCD | BCD0 | CD00 | unused shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_USHR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_INPUT, 0, 1)); shader_code_.push_back(uint32_t(InOutRegister::kVSInVertexIndex)); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(8); shader_code_.push_back(16); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.uint_instruction_count; // 8-in-16: Insert A in Y. // bfi reg.y, l(8), l(8), reg.x, reg.y // ABCD | BAD0 | CD00 | unused shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_BFI) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(11)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(8); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(8); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(reg); ++stat_.instruction_count; ++stat_.uint_instruction_count; // 8-in-16: Insert C in W. // bfi reg.y, l(8), l(24), reg.z, reg.y // ABCD | BADC | CD00 | unused shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_BFI) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(11)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(8); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(24); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(reg); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Get bits indicating what swaps should be done. // ubfe reg.zw, l(0, 0, 1, 1).zw, l(0, 0, 0, 1).zw, xe_vertex_index_endian.xx // ABCD | BADC | 8in16/16in32? | 8in32/16in32? system_constants_used_ |= 1ull << kSysConst_VertexIndexEndian_Index; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_UBFE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(17)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1100, 1)); shader_code_.push_back(reg); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(1); shader_code_.push_back(1); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(1); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSysConst_VertexIndexEndian_Comp, 3)); shader_code_.push_back(uint32_t(cbuffer_index_system_constants_)); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_VertexIndexEndian_Vec); ++stat_.instruction_count; ++stat_.uint_instruction_count; // 16-in-32 is used as intermediate swapping step here rather than 8-in-32. // Thus 8-in-16 needs to be done for 8-in-16 (01) and 8-in-32 (10). // And 16-in-32 needs to be done for 8-in-32 (10) and 16-in-32 (11). // xor reg.z, reg.z, reg.w // ABCD | BADC | 8in16/8in32? | 8in32/16in32? shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_XOR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(reg); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Write the 8-in-16 value to X if needed. // movc reg.x, reg.z, reg.y, reg.x // ABCD/BADC | unused | unused | 8in32/16in32? shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(reg); ++stat_.instruction_count; ++stat_.movc_instruction_count; // 16-in-32: Write the low 16 bits. // ushr reg.y, reg.x, l(16) // ABCD/BADC | CD00/DC00 | unused | 8in32/16in32? shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_USHR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(16); ++stat_.instruction_count; ++stat_.uint_instruction_count; // 16-in-32: Write the high 16 bits. // bfi reg.y, l(16), l(16), reg.x, reg.y // ABCD/BADC | CDAB/DCBA | unused | 8in32/16in32? shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_BFI) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(11)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(16); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(16); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(reg); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Apply the 16-in-32 swap if needed. // movc reg.x, reg.w, reg.y, reg.x shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(reg); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Add the base vertex index. system_constants_used_ |= 1ull << kSysConst_VertexBaseIndex_Index; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSysConst_VertexBaseIndex_Comp, 3)); shader_code_.push_back(uint32_t(cbuffer_index_system_constants_)); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_VertexBaseIndex_Vec); ++stat_.instruction_count; ++stat_.int_instruction_count; // Convert to float and replicate the swapped value in the destination // register (what should be in YZW is unknown, but just to make it a bit // cleaner). shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ITOF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(reg); ++stat_.instruction_count; ++stat_.conversion_instruction_count; if (IndexableGPRsUsed()) { // Store to indexed GPR 0 in x0[0]. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(6)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_INDEXABLE_TEMP, 0b1111, 2)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(reg); ++stat_.instruction_count; ++stat_.array_instruction_count; PopSystemTemp(); } } void DxbcShaderTranslator::StartVertexShader() { // Zero the interpolators. for (uint32_t i = 0; i < kInterpolatorCount; ++i) { shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(8)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_OUTPUT, 0b1111, 1)); shader_code_.push_back(uint32_t(InOutRegister::kVSOutInterpolators) + i); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.mov_instruction_count; } // Zero the point coordinate (will be set in the geometry shader if needed) // and set the point size to a negative value to tell the geometry shader that // it should use the global point size - the vertex shader may overwrite it // later. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(8)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_OUTPUT, 0b0111, 1)); shader_code_.push_back(uint32_t(InOutRegister::kVSOutPointParameters)); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); // -1.0f shader_code_.push_back(0xBF800000u); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.mov_instruction_count; // Write the vertex index to GPR 0. StartVertexShader_LoadVertexIndex(); } void DxbcShaderTranslator::StartPixelShader() { if (edram_rov_used_) { // Copy depth to the system register. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_INSTRUCTION_SATURATE(1) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_INPUT, 2, 1)); shader_code_.push_back(uint32_t(InOutRegister::kPSInPosition)); ++stat_.instruction_count; ++stat_.mov_instruction_count; } // If not translating anything, we only need the depth. if (is_depth_only_pixel_shader_) { return; } // Copy interpolants to GPRs. uint32_t interpolator_count = std::min(kInterpolatorCount, register_count()); if (IndexableGPRsUsed()) { // Copy through r# to x0[#]. uint32_t interpolator_temp_register = PushSystemTemp(); for (uint32_t i = 0; i < interpolator_count; ++i) { shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(interpolator_temp_register); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_INPUT, kSwizzleXYZW, 1)); shader_code_.push_back(uint32_t(InOutRegister::kPSInInterpolators) + i); ++stat_.instruction_count; ++stat_.mov_instruction_count; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(6)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_INDEXABLE_TEMP, 0b1111, 2)); shader_code_.push_back(0); shader_code_.push_back(i); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(interpolator_temp_register); ++stat_.instruction_count; ++stat_.array_instruction_count; } PopSystemTemp(); } else { // Copy directly to r#. for (uint32_t i = 0; i < interpolator_count; ++i) { shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(i); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_INPUT, kSwizzleXYZW, 1)); shader_code_.push_back(uint32_t(InOutRegister::kPSInInterpolators) + i); ++stat_.instruction_count; ++stat_.mov_instruction_count; } } // Write screen and point coordinates to the specified interpolator register // (ps_param_gen). uint32_t param_gen_select_temp = PushSystemTemp(); uint32_t param_gen_value_temp = PushSystemTemp(); // Check if they need to be written. system_constants_used_ |= 1ull << kSysConst_PixelPosReg_Index; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ULT) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(param_gen_select_temp); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSysConst_PixelPosReg_Comp, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_PixelPosReg_Vec); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(interpolator_count); ++stat_.instruction_count; ++stat_.uint_instruction_count; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IF) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN( D3D10_SB_INSTRUCTION_TEST_NONZERO) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(param_gen_select_temp); ++stat_.instruction_count; ++stat_.dynamic_flow_control_count; // Write VPOS (without supersampling because SSAA is used to fake MSAA, and // at integer coordinates rather than half-pixel if needed) to XY. system_constants_used_ |= 1ull << kSysConst_SSAAInvScale_Index; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MUL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0011, 1)); shader_code_.push_back(param_gen_value_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_INPUT, kSwizzleXYZW, 1)); shader_code_.push_back(uint32_t(InOutRegister::kPSInPosition)); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSysConst_SSAAInvScale_Comp | ((kSysConst_SSAAInvScale_Comp + 1) << 2), 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_SSAAInvScale_Vec); ++stat_.instruction_count; ++stat_.float_instruction_count; // Floor VPOS so with AA, the resulting pixel corner/center is written, not // the sample position (games likely don't expect it to be fractional). shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ROUND_NI) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0011, 1)); shader_code_.push_back(param_gen_value_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(param_gen_value_temp); ++stat_.instruction_count; ++stat_.float_instruction_count; // If in OpenGL half-pixel offset mode, write the center of the pixel. system_constants_used_ |= 1ull << kSysConst_PixelHalfPixelOffset_Index; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0011, 1)); shader_code_.push_back(param_gen_value_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(param_gen_value_temp); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSysConst_PixelHalfPixelOffset_Comp, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_PixelHalfPixelOffset_Vec); ++stat_.instruction_count; ++stat_.float_instruction_count; // Write point sprite coordinates to ZW. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1100, 1)); shader_code_.push_back(param_gen_value_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_INPUT, 0b01000000, 1)); shader_code_.push_back(uint32_t(InOutRegister::kPSInPointParameters)); ++stat_.instruction_count; ++stat_.mov_instruction_count; if (IndexableGPRsUsed()) { // Copy the register index to an r# so it can be used for indexable temp // addressing. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(8)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(param_gen_select_temp); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSysConst_PixelPosReg_Comp, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_PixelPosReg_Vec); ++stat_.instruction_count; ++stat_.mov_instruction_count; // Store the value to an x0[xe_pixel_pos_reg]. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_INDEXABLE_TEMP, 0b1111, 2, D3D10_SB_OPERAND_INDEX_IMMEDIATE32, D3D10_SB_OPERAND_INDEX_RELATIVE)); shader_code_.push_back(0); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(param_gen_select_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(param_gen_value_temp); ++stat_.instruction_count; ++stat_.array_instruction_count; } else { // Store to the needed register using movc. for (uint32_t i = 0; i < interpolator_count; ++i) { if ((i & 3) == 0) { // Get a mask of whether the current register index is the target one. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IEQ) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(12)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(param_gen_select_temp); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSysConst_PixelPosReg_Comp, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_PixelPosReg_Vec); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(i); shader_code_.push_back(i + 1); shader_code_.push_back(i + 2); shader_code_.push_back(i + 3); ++stat_.instruction_count; ++stat_.int_instruction_count; } shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(i); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, i & 3, 1)); shader_code_.push_back(param_gen_select_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(param_gen_value_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(i); ++stat_.instruction_count; ++stat_.movc_instruction_count; } } shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; // Release param_gen_select_temp and param_gen_value_temp. PopSystemTemp(2); } void DxbcShaderTranslator::StartTranslation() { // Allocate global system temporary registers that may also be used in the // epilogue. if (IsDXBCVertexShader()) { system_temp_position_ = PushSystemTemp(true); } else if (IsDXBCPixelShader()) { if (!is_depth_only_pixel_shader_) { for (uint32_t i = 0; i < 4; ++i) { // In the ROV path, no need to initialize the colors because original // values will be kept for the unwritten components. system_temp_color_[i] = PushSystemTemp(!edram_rov_used_); } } if (edram_rov_used_) { if (!is_depth_only_pixel_shader_) { system_temp_color_written_ = PushSystemTemp(true); } system_temp_depth_ = PushSystemTemp(); } } if (!is_depth_only_pixel_shader_) { // Allocate system temporary variables for the translated code. system_temp_pv_ = PushSystemTemp(true); system_temp_ps_pc_p0_a0_ = PushSystemTemp(true); system_temp_aL_ = PushSystemTemp(true); system_temp_loop_count_ = PushSystemTemp(true); system_temp_grad_h_lod_ = PushSystemTemp(true); system_temp_grad_v_ = PushSystemTemp(true); } // Write stage-specific prologue. if (IsDXBCVertexShader()) { StartVertexShader(); } else if (IsDXBCPixelShader()) { StartPixelShader(); } // If not translating anything, don't start the main loop. if (is_depth_only_pixel_shader_) { return; } // Start the main loop (for jumping to labels by setting pc and continuing). shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_LOOP) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; ++stat_.dynamic_flow_control_count; // Switch and the first label (pc == 0). if (FLAGS_dxbc_switch) { shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_SWITCH) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); ++stat_.instruction_count; ++stat_.dynamic_flow_control_count; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_CASE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.static_flow_control_count; } else { shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IF) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN( D3D10_SB_INSTRUCTION_TEST_ZERO) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); ++stat_.instruction_count; ++stat_.dynamic_flow_control_count; } } void DxbcShaderTranslator::CompleteVertexShader() { // Get what we need to do with the position. uint32_t ndc_control_temp = PushSystemTemp(); system_constants_used_ |= 1ull << kSysConst_Flags_Index; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(12)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(ndc_control_temp); shader_code_.push_back(EncodeVectorReplicatedOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSysConst_Flags_Comp, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_Flags_Vec); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(kSysFlag_XYDividedByW); shader_code_.push_back(kSysFlag_ZDividedByW); shader_code_.push_back(kSysFlag_WNotReciprocal); shader_code_.push_back(kSysFlag_ReverseZ); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Revert getting the reciprocal of W and dividing XY by W if needed. // TODO(Triang3l): Check if having XY or Z pre-divided by W should enable // affine interpolation. uint32_t w_format_temp = PushSystemTemp(); // If the shader has returned 1/W, restore W. First take the reciprocal, which // may be either W (what we need) or 1/W, depending on the vertex W format. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_RCP) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(w_format_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(system_temp_position_); ++stat_.instruction_count; ++stat_.float_instruction_count; // Then, if the shader returns 1/W (vtx_w0_fmt is 0), write 1/(1/W) to the // position. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1000, 1)); shader_code_.push_back(system_temp_position_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(ndc_control_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(system_temp_position_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(w_format_temp); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Multiply XYZ by W in case the shader returns XYZ/W and we'll need to // restore XYZ. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MUL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(w_format_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_position_); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(system_temp_position_); ++stat_.instruction_count; ++stat_.float_instruction_count; // If vtx_xy_fmt and/or vtx_z_fmt are 1, XY and/or Z are pre-divided by W. // Restore them in this case. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(system_temp_position_); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b00010000, 1)); shader_code_.push_back(ndc_control_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(w_format_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_position_); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Release w_format_temp. PopSystemTemp(); // Apply scale for drawing without a viewport, and also remap from OpenGL // Z clip space to Direct3D if needed. system_constants_used_ |= 1ull << kSysConst_NDCScale_Index; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MUL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(system_temp_position_); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_position_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSysConst_NDCScale_Comp | ((kSysConst_NDCScale_Comp + 1) << 2) | ((kSysConst_NDCScale_Comp + 2) << 4), 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_NDCScale_Vec); ++stat_.instruction_count; ++stat_.float_instruction_count; // Reverse Z (Z = W - Z) if the viewport depth is inverted. uint32_t reverse_z_temp = PushSystemTemp(); shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(8)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(reverse_z_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(system_temp_position_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1) | ENCODE_D3D10_SB_OPERAND_EXTENDED(1)); shader_code_.push_back( ENCODE_D3D10_SB_EXTENDED_OPERAND_MODIFIER(D3D10_SB_OPERAND_MODIFIER_NEG)); shader_code_.push_back(system_temp_position_); ++stat_.instruction_count; ++stat_.float_instruction_count; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1)); shader_code_.push_back(system_temp_position_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(ndc_control_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(reverse_z_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(system_temp_position_); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Release reverse_z_temp. PopSystemTemp(); // Release ndc_control_temp. PopSystemTemp(); // Apply offset (multiplied by W) for drawing without a viewport and for half // pixel offset. system_constants_used_ |= 1ull << kSysConst_NDCOffset_Index; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(11)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(system_temp_position_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSysConst_NDCOffset_Comp | ((kSysConst_NDCOffset_Comp + 1) << 2) | ((kSysConst_NDCOffset_Comp + 2) << 4), 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_NDCOffset_Vec); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(system_temp_position_); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_position_); ++stat_.instruction_count; ++stat_.float_instruction_count; // Write the position to the output. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_OUTPUT, 0b1111, 1)); shader_code_.push_back(uint32_t(InOutRegister::kVSOutPosition)); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_position_); ++stat_.instruction_count; ++stat_.mov_instruction_count; } void DxbcShaderTranslator::CompletePixelShader_DepthTo24Bit() { // Unpack the depth format. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(system_temp_depth_); system_constants_used_ |= 1ull << kSysConst_Flags_Index; shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSysConst_Flags_Comp, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_Flags_Vec); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(kSysFlag_DepthFloat24); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Convert according to the format. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IF) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN( D3D10_SB_INSTRUCTION_TEST_NONZERO) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(system_temp_depth_); ++stat_.instruction_count; ++stat_.dynamic_flow_control_count; // *************************************************************************** // 20e4 conversion begins here. // 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). // Calculate the denormalized value if the number is too small to be // represented as normalized 20e4 into Y. // y = f32 & 0x7FFFFF shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0x7FFFFF); ++stat_.instruction_count; ++stat_.uint_instruction_count; // y = (f32 & 0x7FFFFF) | 0x800000 shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_OR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0x800000); ++stat_.instruction_count; ++stat_.uint_instruction_count; // z = f32 >> 23 shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_USHR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(23); ++stat_.instruction_count; ++stat_.uint_instruction_count; // z = 113 - (f32 >> 23) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(8)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(113); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1) | ENCODE_D3D10_SB_OPERAND_EXTENDED(1)); shader_code_.push_back( ENCODE_D3D10_SB_EXTENDED_OPERAND_MODIFIER(D3D10_SB_OPERAND_MODIFIER_NEG)); shader_code_.push_back(system_temp_depth_); ++stat_.instruction_count; ++stat_.int_instruction_count; // y = ((f32 & 0x7FFFFF) | 0x800000) >> (113 - (f32 >> 23)) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_USHR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(system_temp_depth_); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Check if the number is too small to be represented as normalized 20e4. // z = f32 < 0x38800000 shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ULT) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0x38800000); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Bias the exponent. // f32 += 0xC8000000 shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0xC8000000u); ++stat_.instruction_count; ++stat_.int_instruction_count; // Replace the number in f32 with a denormalized one if needed. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_depth_); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Build the 20e4 number. // y = f32 >> 3 shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_USHR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(3); ++stat_.instruction_count; ++stat_.uint_instruction_count; // y = (f32 >> 3) & 1 shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(1); ++stat_.instruction_count; ++stat_.uint_instruction_count; // f24 = f32 + 3 shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(3); ++stat_.instruction_count; ++stat_.int_instruction_count; // f24 = f32 + 3 + ((f32 >> 3) & 1) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(system_temp_depth_); ++stat_.instruction_count; ++stat_.int_instruction_count; // f24 = (f32 + 3 + ((f32 >> 3) & 1)) >> 3 shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_USHR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(3); ++stat_.instruction_count; ++stat_.uint_instruction_count; // f24 = ((f32 + 3 + ((f32 >> 3) & 1)) >> 3) & 0xFFFFFF shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0xFFFFFF); ++stat_.instruction_count; ++stat_.uint_instruction_count; // *************************************************************************** // 20e4 conversion ends here. // *************************************************************************** shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ELSE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; // *************************************************************************** // Unorm24 conversion begins here. // *************************************************************************** // Multiply by float(0xFFFFFF). shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MUL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0x4B7FFFFF); ++stat_.instruction_count; ++stat_.float_instruction_count; // Round to the nearest integer. This is the correct way of rounding, rounding // towards zero gives 0xFF instead of 0x100 in clear shaders in, for instance, // Halo 3. // https://docs.microsoft.com/en-us/windows/desktop/direct3d10/d3d10-graphics-programming-guide-resources-data-conversion shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ROUND_NE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_depth_); ++stat_.instruction_count; ++stat_.float_instruction_count; // Convert to fixed-point. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_FTOU) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_depth_); ++stat_.instruction_count; ++stat_.conversion_instruction_count; // *************************************************************************** // Unorm24 conversion ends here. // *************************************************************************** shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; } void DxbcShaderTranslator::CompletePixelShader_GammaCorrect(uint32_t color_temp, bool to_gamma) { uint32_t pieces_temp = PushSystemTemp(); for (uint32_t j = 0; j < 3; ++j) { // Calculate how far we are on each piece of the curve. Multiply by 1/width // of each piece, subtract start/width of it and saturate. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAD) | ENCODE_D3D10_SB_INSTRUCTION_SATURATE(1) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(15)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(pieces_temp); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, j, 1)); shader_code_.push_back(color_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); if (to_gamma) { // 1.0 / 0.0625 shader_code_.push_back(0x41800000u); // 1.0 / 0.0625 shader_code_.push_back(0x41800000u); // 1.0 / 0.375 shader_code_.push_back(0x402AAAABu); // 1.0 / 0.5 shader_code_.push_back(0x40000000u); } else { // 1.0 / 0.25 shader_code_.push_back(0x40800000u); // 1.0 / 0.125 shader_code_.push_back(0x41000000u); // 1.0 / 0.375 shader_code_.push_back(0x402AAAABu); // 1.0 / 0.25 shader_code_.push_back(0x40800000u); } shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); if (to_gamma) { // -0.0 / 0.0625 shader_code_.push_back(0); // -0.0625 / 0.0625 shader_code_.push_back(0xBF800000u); // -0.125 / 0.375 shader_code_.push_back(0xBEAAAAABu); // -0.5 / 0.5 shader_code_.push_back(0xBF800000u); } else { // -0.0 / 0.25 shader_code_.push_back(0); // -0.25 / 0.125 shader_code_.push_back(0xC0000000u); // -0.375 / 0.375 shader_code_.push_back(0xBF800000u); // -0.75 / 0.25 shader_code_.push_back(0xC0400000u); } ++stat_.instruction_count; ++stat_.float_instruction_count; // Combine the contribution of all pieces to the resulting value - multiply // each piece by slope*width and sum them. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DP4) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1 << j, 1)); shader_code_.push_back(color_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(pieces_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); if (to_gamma) { // 4.0 * 0.0625 shader_code_.push_back(0x3E800000u); // 2.0 * 0.0625 shader_code_.push_back(0x3E000000u); // 1.0 * 0.375 shader_code_.push_back(0x3EC00000u); // 0.5 * 0.5 shader_code_.push_back(0x3E800000u); } else { // 0.25 * 0.25 shader_code_.push_back(0x3D800000u); // 0.5 * 0.125 shader_code_.push_back(0x3D800000u); // 1.0 * 0.375 shader_code_.push_back(0x3EC00000u); // 2.0 * 0.25 shader_code_.push_back(0x3F000000u); } ++stat_.instruction_count; ++stat_.float_instruction_count; } // Release pieces_temp. PopSystemTemp(); } void DxbcShaderTranslator::CompletePixelShader_WriteToRTVs() { // 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(); system_constants_used_ |= 1ull << kSysConst_Flags_Index; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(12)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(gamma_temp); shader_code_.push_back(EncodeVectorReplicatedOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSysConst_Flags_Comp, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_Flags_Vec); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(kSysFlag_Color0Gamma); shader_code_.push_back(kSysFlag_Color1Gamma); shader_code_.push_back(kSysFlag_Color2Gamma); shader_code_.push_back(kSysFlag_Color3Gamma); ++stat_.instruction_count; ++stat_.uint_instruction_count; for (uint32_t i = 0; i < 4; ++i) { shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IF) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN( D3D10_SB_INSTRUCTION_TEST_NONZERO) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, i, 1)); shader_code_.push_back(gamma_temp); ++stat_.instruction_count; ++stat_.dynamic_flow_control_count; CompletePixelShader_GammaCorrect(system_temp_color_[i], true); shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; } // 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) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IEQ) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(12)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(remap_movc_mask_temp); shader_code_.push_back(EncodeVectorReplicatedOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, i, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_ColorOutputMap_Vec); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(1); shader_code_.push_back(2); shader_code_.push_back(3); ++stat_.instruction_count; ++stat_.int_instruction_count; for (uint32_t j = 0; j < 4; ++j) { // If map.i == j, move guest color j to the temporary host color. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(remap_movc_target_temp); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, j, 1)); shader_code_.push_back(remap_movc_mask_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_color_[j]); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(remap_movc_target_temp); ++stat_.instruction_count; ++stat_.movc_instruction_count; } // Write the remapped color to host render target i. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_OUTPUT, 0b1111, 1)); shader_code_.push_back(i); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(remap_movc_target_temp); ++stat_.instruction_count; ++stat_.mov_instruction_count; } // Release remap_movc_mask_temp and remap_movc_target_temp. PopSystemTemp(2); } void DxbcShaderTranslator::CompletePixelShader_WriteToROV_ExtractPackLayout( uint32_t rt_index, bool high, uint32_t width_temp, uint32_t offset_temp) { if (high) { system_constants_used_ |= (1ull << kSysConst_EDRAMRTPackWidthHigh_Index) | (1ull << kSysConst_EDRAMRTPackOffsetHigh_Index); } else { system_constants_used_ |= (1ull << kSysConst_EDRAMRTPackWidthLow_Index) | (1ull << kSysConst_EDRAMRTPackOffsetLow_Index); } for (uint32_t i = 0; i < 2; ++i) { shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_UBFE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(17)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(i ? offset_temp : width_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(8); shader_code_.push_back(8); shader_code_.push_back(8); shader_code_.push_back(8); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(8); shader_code_.push_back(16); shader_code_.push_back(24); shader_code_.push_back(EncodeVectorReplicatedOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, rt_index, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); if (i) { shader_code_.push_back(high ? kSysConst_EDRAMRTPackOffsetHigh_Vec : kSysConst_EDRAMRTPackOffsetLow_Vec); } else { shader_code_.push_back(high ? kSysConst_EDRAMRTPackWidthHigh_Vec : kSysConst_EDRAMRTPackWidthLow_Vec); } ++stat_.instruction_count; ++stat_.uint_instruction_count; } } void DxbcShaderTranslator::CompletePixelShader_WriteToROV_LoadColor( uint32_t edram_dword_offset_low_temp, uint32_t edram_dword_offset_high_temp, uint32_t rt_index, uint32_t rt_format_flags_temp, uint32_t target_temp) { // For indexing of the format constants. uint32_t rt_pair_index = rt_index >> 1; uint32_t rt_pair_swizzle = rt_index & 1 ? 0b11101010 : 0b01000000; // Allocate temporary registers for unpacking pixels. uint32_t pack_width_temp = PushSystemTemp(); uint32_t pack_offset_temp = PushSystemTemp(); // Load the lower 32 bits. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_LD_UAV_TYPED) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(8)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(target_temp); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, rt_index, 1)); shader_code_.push_back(edram_dword_offset_low_temp); shader_code_.push_back(EncodeVectorReplicatedOperand( D3D11_SB_OPERAND_TYPE_UNORDERED_ACCESS_VIEW, 0, 2)); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.texture_load_instructions; // Unpack the lower 32 bits, as signed because of k_16_16 and k_16_16_16_16 // (will be masked later if needed). CompletePixelShader_WriteToROV_ExtractPackLayout( rt_index, false, pack_width_temp, pack_offset_temp); shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_IBFE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(target_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(pack_width_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(pack_offset_temp); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(target_temp); ++stat_.instruction_count; ++stat_.int_instruction_count; // Check if need to load the upper 32 bits. system_constants_used_ |= 1ull << kSysConst_EDRAMRTPackWidthHigh_Index; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IF) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN( D3D10_SB_INSTRUCTION_TEST_NONZERO) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, rt_index, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_EDRAMRTPackWidthHigh_Vec); ++stat_.instruction_count; ++stat_.dynamic_flow_control_count; // Allocate a register for the components from the upper 32 bits (will be // combined with the lower using OR). uint32_t high_temp = PushSystemTemp(); // Load the upper 32 bits. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_LD_UAV_TYPED) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(8)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(high_temp); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, rt_index, 1)); shader_code_.push_back(edram_dword_offset_high_temp); shader_code_.push_back(EncodeVectorReplicatedOperand( D3D11_SB_OPERAND_TYPE_UNORDERED_ACCESS_VIEW, 0, 2)); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.texture_load_instructions; // Unpack the higher 32 bits. CompletePixelShader_WriteToROV_ExtractPackLayout( rt_index, true, pack_width_temp, pack_offset_temp); shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_IBFE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(high_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(pack_width_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(pack_offset_temp); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(high_temp); ++stat_.instruction_count; ++stat_.int_instruction_count; // Combine the components from the lower and the upper 32 bits. In ibfe, if // width is 0, the result is 0 (not 0xFFFFFFFF), so it's fine to do this // without pre-masking. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_OR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(target_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(target_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(high_temp); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Release high_temp. PopSystemTemp(); // Upper 32 bits loaded. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; // Release pack_width_temp and pack_offset_temp. PopSystemTemp(2); // Mask the components to differentiate between signed and unsigned. system_constants_used_ |= (1ull << kSysConst_EDRAMLoadMaskRT01_Index) << rt_pair_index; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(target_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(target_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, rt_pair_swizzle, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_EDRAMLoadMaskRT01_Vec + rt_pair_index); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Convert from fixed-point. uint32_t fixed_temp = PushSystemTemp(); shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ITOF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(fixed_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(target_temp); ++stat_.instruction_count; ++stat_.conversion_instruction_count; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(target_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kROVRTFormatFlagTemp_Fixed_Swizzle, 1)); shader_code_.push_back(rt_format_flags_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(fixed_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(target_temp); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Release fixed_temp. PopSystemTemp(); // *************************************************************************** // 7e3 conversion begins here. // https://github.com/Microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexConvert.cpp // *************************************************************************** // Check if the target format is 7e3 and the conversion is needed (this is // pretty long, better to branch here). shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IF) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN( D3D10_SB_INSTRUCTION_TEST_NONZERO) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_TEMP, kROVRTFormatFlagTemp_Float10, 1)); shader_code_.push_back(rt_format_flags_temp); ++stat_.instruction_count; ++stat_.dynamic_flow_control_count; uint32_t f10_mantissa_temp = PushSystemTemp(); uint32_t f10_exponent_temp = PushSystemTemp(); uint32_t f10_denormalized_temp = PushSystemTemp(); // Extract the mantissa. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(f10_mantissa_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(target_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0x7F); shader_code_.push_back(0x7F); shader_code_.push_back(0x7F); shader_code_.push_back(0x7F); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Extract the exponent. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_USHR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(f10_exponent_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(target_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(7); shader_code_.push_back(7); shader_code_.push_back(7); shader_code_.push_back(7); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Normalize the mantissa for denormalized numbers (with zero exponent - // exponent can be used for selection in movc). // Note that HLSL firstbithigh(x) is compiled to DXBC like: // `x ? 31 - firstbit_hi(x) : -1` // (it returns the index from the LSB, not the MSB, but -1 for zero as well). // denormalized_temp = firstbit_hi(mantissa) shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_FIRSTBIT_HI) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(f10_denormalized_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(f10_mantissa_temp); ++stat_.instruction_count; ++stat_.uint_instruction_count; // denormalized_temp = 7 - (31 - firstbit_hi(mantissa)) // Or, if expanded: // denormalized_temp = firstbit_hi(mantissa) - 24 shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(f10_denormalized_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(f10_denormalized_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(uint32_t(-24)); shader_code_.push_back(uint32_t(-24)); shader_code_.push_back(uint32_t(-24)); shader_code_.push_back(uint32_t(-24)); ++stat_.instruction_count; ++stat_.int_instruction_count; // If mantissa is zero, then: // denormalized_temp = 7 - (-1) = 8 // After this, it works like the following HLSL: // denormalized_temp = 7 - firstbithigh(mantissa) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(12)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(f10_denormalized_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(f10_mantissa_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(f10_denormalized_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(8); shader_code_.push_back(8); shader_code_.push_back(8); shader_code_.push_back(8); ++stat_.instruction_count; ++stat_.movc_instruction_count; // If the number is not denormalized, make // `(mantissa << (7 - firstbithigh(mantissa))) & 0x7F` // a no-op - zero 7 - firstbithigh(mantissa). shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(12)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(f10_denormalized_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(f10_exponent_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(f10_denormalized_temp); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Normalize the mantissa - step 1. // mantissa = mantissa << (7 - firstbithigh(mantissa)) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ISHL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(f10_mantissa_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(f10_mantissa_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(f10_denormalized_temp); ++stat_.instruction_count; ++stat_.int_instruction_count; // Normalize the mantissa - step 2. // mantissa = (mantissa << (7 - firstbithigh(mantissa))) & 0x7F shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(f10_mantissa_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(f10_mantissa_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0x7F); shader_code_.push_back(0x7F); shader_code_.push_back(0x7F); shader_code_.push_back(0x7F); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Get the normalized exponent. // denormalized_temp = 1 - (7 - firstbithigh(mantissa)) // If the number is normal, the result will be ignored anyway, so zeroing // 7 - firstbithigh(mantissa) will have no effect on this. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(11)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(f10_denormalized_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(1); shader_code_.push_back(1); shader_code_.push_back(1); shader_code_.push_back(1); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1) | ENCODE_D3D10_SB_OPERAND_EXTENDED(1)); shader_code_.push_back( ENCODE_D3D10_SB_EXTENDED_OPERAND_MODIFIER(D3D10_SB_OPERAND_MODIFIER_NEG)); shader_code_.push_back(f10_denormalized_temp); ++stat_.instruction_count; ++stat_.int_instruction_count; // Overwrite the exponent with the normalized one if needed. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(f10_exponent_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(f10_exponent_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(f10_exponent_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(f10_denormalized_temp); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Bias the exponent. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(f10_exponent_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(f10_exponent_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(124); shader_code_.push_back(124); shader_code_.push_back(124); shader_code_.push_back(124); ++stat_.instruction_count; ++stat_.int_instruction_count; // If the original number is zero, make the exponent zero (mantissa is already // zero in this case). shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(12)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(f10_exponent_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(target_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(f10_exponent_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Shift the mantissa into its float32 position. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ISHL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(f10_mantissa_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(f10_mantissa_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(16); shader_code_.push_back(16); shader_code_.push_back(16); shader_code_.push_back(16); ++stat_.instruction_count; ++stat_.int_instruction_count; // Shift the exponent into its float32 position. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ISHL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(f10_exponent_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(f10_exponent_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(23); shader_code_.push_back(23); shader_code_.push_back(23); shader_code_.push_back(23); ++stat_.instruction_count; ++stat_.int_instruction_count; // Combine mantissa and exponent into float32 numbers. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_OR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(target_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(f10_mantissa_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(f10_exponent_temp); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Release f10_mantissa_temp, f10_exponent_temp and f10_denormalized_temp. PopSystemTemp(3); // 7e3 conversion done. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; // *************************************************************************** // 7e3 conversion ends here. // *************************************************************************** // Convert from 16-bit float. uint32_t f16_temp = PushSystemTemp(); shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_F16TOF32) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(f16_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(target_temp); ++stat_.instruction_count; ++stat_.conversion_instruction_count; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(target_temp); shader_code_.push_back(EncodeVectorReplicatedOperand( D3D10_SB_OPERAND_TYPE_TEMP, kROVRTFormatFlagTemp_Float16, 1)); shader_code_.push_back(rt_format_flags_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(f16_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(target_temp); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Release f16_temp. PopSystemTemp(); // Scale by the fixed-point conversion factor. system_constants_used_ |= (1ull << kSysConst_EDRAMLoadScaleRT01_Index) << rt_pair_index; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MUL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(target_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(target_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, rt_pair_swizzle, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_EDRAMLoadScaleRT01_Vec + rt_pair_index); ++stat_.instruction_count; ++stat_.float_instruction_count; } void DxbcShaderTranslator::CompletePixelShader_WriteToROV_ExtractBlendScales( uint32_t rt_index, uint32_t constant_swizzle, bool is_signed, uint32_t shift_x, uint32_t shift_y, uint32_t shift_z, uint32_t shift_w, uint32_t target_temp, uint32_t write_mask) { uint32_t rt_pair_index = rt_index >> 1; if (rt_index & 1) { constant_swizzle |= 0b10101010; } // Sign-extend 2 bits for signed, extract 1 bit for unsigned. system_constants_used_ |= (1ull << kSysConst_EDRAMBlendRT01_Index) << rt_pair_index; shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(is_signed ? D3D11_SB_OPCODE_IBFE : D3D11_SB_OPCODE_UBFE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(17)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, write_mask, 1)); shader_code_.push_back(target_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); uint32_t width = is_signed ? 2 : 1; shader_code_.push_back(width); shader_code_.push_back(width); shader_code_.push_back(width); shader_code_.push_back(width); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(shift_x); shader_code_.push_back(shift_y); shader_code_.push_back(shift_z); shader_code_.push_back(shift_w); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, constant_swizzle, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_EDRAMBlendRT01_Vec + rt_pair_index); ++stat_.instruction_count; if (is_signed) { ++stat_.int_instruction_count; } else { ++stat_.uint_instruction_count; } // Convert -1, 0 or 1 integer to float. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(is_signed ? D3D10_SB_OPCODE_ITOF : D3D10_SB_OPCODE_UTOF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, write_mask, 1)); shader_code_.push_back(target_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(target_temp); ++stat_.instruction_count; ++stat_.conversion_instruction_count; } void DxbcShaderTranslator::CompletePixelShader_WriteToROV_ClampColor( uint32_t rt_index, uint32_t color_in_temp, uint32_t color_out_temp) { uint32_t rt_pair_index = rt_index >> 1; uint32_t rt_pair_swizzle = rt_index & 1 ? 0b11101010 : 0b01000000; system_constants_used_ |= (1ull << kSysConst_EDRAMStoreMinRT01_Index) << rt_pair_index; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAX) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(color_out_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(color_in_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, rt_pair_swizzle, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_EDRAMStoreMinRT01_Vec + rt_pair_index); ++stat_.instruction_count; ++stat_.float_instruction_count; system_constants_used_ |= (1ull << kSysConst_EDRAMStoreMaxRT01_Index) << rt_pair_index; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MIN) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(color_out_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(color_out_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, rt_pair_swizzle, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_EDRAMStoreMaxRT01_Vec + rt_pair_index); ++stat_.instruction_count; ++stat_.float_instruction_count; } void DxbcShaderTranslator::CompletePixelShader_WriteToROV_ApplyZeroBlendScale( uint32_t scale_temp, uint32_t scale_swizzle, uint32_t factor_in_temp, uint32_t factor_swizzle, uint32_t factor_out_temp, uint32_t write_mask) { shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(12)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, write_mask, 1)); shader_code_.push_back(factor_out_temp); shader_code_.push_back(EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, scale_swizzle, 1)); shader_code_.push_back(scale_temp); shader_code_.push_back(EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, factor_swizzle, 1)); shader_code_.push_back(factor_in_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.movc_instruction_count; } void DxbcShaderTranslator::CompletePixelShader_WriteToROV_Blend( uint32_t rt_index, uint32_t rt_format_flags_temp, uint32_t src_color_and_output_temp, uint32_t dest_color_temp) { // Temporary register for scales of things that contribute to the blending, // usually -1.0, 0.0 or 1.0. uint32_t scale_temp = PushSystemTemp(); // Temporary register for making 0 * Infinity result in 0 rather than NaN, // for clamping of the source color and the factors, and for applying alpha // saturate factor. uint32_t factor_calculation_temp = PushSystemTemp(); uint32_t src_factor_and_result_temp = PushSystemTemp(); uint32_t dest_factor_and_minmax_temp = PushSystemTemp(); // Clamp the source color if needed. For fixed-point formats, clamping must // always be done, for floating-point, it must not be, however, // k_2_10_10_10_FLOAT has fixed-point alpha. // https://docs.microsoft.com/en-us/windows/desktop/direct3d11/d3d10-graphics-programming-guide-output-merger-stage CompletePixelShader_WriteToROV_ClampColor(rt_index, src_color_and_output_temp, factor_calculation_temp); shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(src_color_and_output_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kROVRTFormatFlagTemp_Fixed_Swizzle, 1)); shader_code_.push_back(rt_format_flags_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(factor_calculation_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(src_color_and_output_temp); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Interleaving source and destination writes when possible to reduce // write-read dependencies. // Constant one for factors, reusing dest_factor_and_minmax_temp (since it's // the last to be modified). CompletePixelShader_WriteToROV_ExtractBlendScales( rt_index, 0b00000000, false, kBlendX_Src_One_Shift, kBlendX_SrcAlpha_One_Shift, kBlendX_Dest_One_Shift, kBlendX_DestAlpha_One_Shift, dest_factor_and_minmax_temp); // Source color for color factors, source alpha for alpha factors, plus ones. // This will initialize src_factor_and_result_temp and // dest_factor_and_minmax_temp. CompletePixelShader_WriteToROV_ExtractBlendScales( rt_index, 0b00000000, true, kBlendX_Src_SrcColor_Shift, kBlendX_SrcAlpha_SrcAlpha_Shift, kBlendX_Dest_SrcColor_Shift, kBlendX_DestAlpha_SrcAlpha_Shift, scale_temp); for (uint32_t i = 0; i < 2; ++i) { uint32_t swizzle = i ? 0b11101010 : 0b01000000; CompletePixelShader_WriteToROV_ApplyZeroBlendScale( scale_temp, swizzle, src_color_and_output_temp, kSwizzleXYZW, factor_calculation_temp); shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(i ? dest_factor_and_minmax_temp : src_factor_and_result_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(factor_calculation_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, swizzle, 1)); shader_code_.push_back(scale_temp); // dest_factor_and_minmax_temp is the last one to be modified, so it stores // the ones (not to allocate an additional temporary register). shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, swizzle, 1)); shader_code_.push_back(dest_factor_and_minmax_temp); ++stat_.instruction_count; ++stat_.float_instruction_count; } // Destination color for color factors, destination alpha for alpha factors. CompletePixelShader_WriteToROV_ExtractBlendScales( rt_index, 0b00000000, true, kBlendX_Src_DestColor_Shift, kBlendX_SrcAlpha_DestAlpha_Shift, kBlendX_Dest_DestColor_Shift, kBlendX_DestAlpha_DestAlpha_Shift, scale_temp); for (uint32_t i = 0; i < 2; ++i) { uint32_t swizzle = i ? 0b11101010 : 0b01000000; CompletePixelShader_WriteToROV_ApplyZeroBlendScale( scale_temp, swizzle, dest_color_temp, kSwizzleXYZW, factor_calculation_temp); shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(i ? dest_factor_and_minmax_temp : src_factor_and_result_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(factor_calculation_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, swizzle, 1)); shader_code_.push_back(scale_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(i ? dest_factor_and_minmax_temp : src_factor_and_result_temp); ++stat_.instruction_count; ++stat_.float_instruction_count; } // Source and destination alphas for color factors. CompletePixelShader_WriteToROV_ExtractBlendScales( rt_index, 0b00000000, true, kBlendX_Src_SrcAlpha_Shift, kBlendX_Dest_SrcAlpha_Shift, kBlendX_Src_DestAlpha_Shift, kBlendX_Dest_DestAlpha_Shift, scale_temp); CompletePixelShader_WriteToROV_ApplyZeroBlendScale( scale_temp, kSwizzleXYZW, src_color_and_output_temp, kSwizzleWWWW, factor_calculation_temp, 0b0011); CompletePixelShader_WriteToROV_ApplyZeroBlendScale( scale_temp, kSwizzleXYZW, dest_color_temp, kSwizzleWWWW, factor_calculation_temp, 0b1100); for (uint32_t i = 0; i < 4; ++i) { shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(i & 1 ? dest_factor_and_minmax_temp : src_factor_and_result_temp); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, i, 1)); shader_code_.push_back(factor_calculation_temp); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, i, 1)); shader_code_.push_back(scale_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(i & 1 ? dest_factor_and_minmax_temp : src_factor_and_result_temp); ++stat_.instruction_count; ++stat_.float_instruction_count; } // Constant color for color factors, constant alpha for alpha factors. system_constants_used_ |= 1ull << kSysConst_EDRAMBlendConstant_Index; CompletePixelShader_WriteToROV_ExtractBlendScales( rt_index, 0b01010101, true, kBlendY_Src_ConstantColor_Shift, kBlendY_SrcAlpha_ConstantAlpha_Shift, kBlendY_Dest_ConstantColor_Shift, kBlendY_DestAlpha_ConstantAlpha_Shift, scale_temp); for (uint32_t i = 0; i < 2; ++i) { uint32_t swizzle = i ? 0b11101010 : 0b01000000; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(14)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(factor_calculation_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, swizzle, 1)); shader_code_.push_back(scale_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSwizzleXYZW, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_EDRAMBlendConstant_Vec); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.movc_instruction_count; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(i ? dest_factor_and_minmax_temp : src_factor_and_result_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(factor_calculation_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, swizzle, 1)); shader_code_.push_back(scale_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(i ? dest_factor_and_minmax_temp : src_factor_and_result_temp); ++stat_.instruction_count; ++stat_.float_instruction_count; } // Constant alpha for color factors. CompletePixelShader_WriteToROV_ExtractBlendScales( rt_index, 0b01010101, true, kBlendY_Src_ConstantAlpha_Shift, kBlendY_Dest_ConstantAlpha_Shift, 0, 0, scale_temp, 0b0011); shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(14)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0011, 1)); shader_code_.push_back(factor_calculation_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(scale_temp); shader_code_.push_back(EncodeVectorReplicatedOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, 3, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_EDRAMBlendConstant_Vec); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.movc_instruction_count; for (uint32_t i = 0; i < 2; ++i) { shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(i ? dest_factor_and_minmax_temp : src_factor_and_result_temp); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, i, 1)); shader_code_.push_back(factor_calculation_temp); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, i, 1)); shader_code_.push_back(scale_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(i ? dest_factor_and_minmax_temp : src_factor_and_result_temp); ++stat_.instruction_count; ++stat_.float_instruction_count; } // Alpha saturate mode. // 1) Clamp the alphas to 1 or less. // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ne-d3d12-d3d12_blend for (uint32_t i = 0; i < 2; ++i) { shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAX) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1 << i, 1)); shader_code_.push_back(factor_calculation_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(i ? dest_color_temp : src_color_and_output_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0x3F800000); ++stat_.instruction_count; ++stat_.float_instruction_count; } // 2) Subtract the destination alpha from 1. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(8)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(factor_calculation_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0x3F800000); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1) | ENCODE_D3D10_SB_OPERAND_EXTENDED(1)); shader_code_.push_back( ENCODE_D3D10_SB_EXTENDED_OPERAND_MODIFIER(D3D10_SB_OPERAND_MODIFIER_NEG)); shader_code_.push_back(factor_calculation_temp); ++stat_.instruction_count; ++stat_.float_instruction_count; // 3) Min(source alpha, 1 - destination alpha). shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MIN) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(factor_calculation_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(factor_calculation_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(factor_calculation_temp); ++stat_.instruction_count; ++stat_.float_instruction_count; // 4) Extract whether the source and the destination color factors are // saturate (for alphas, One should be used in this case). system_constants_used_ |= (1ull << kSysConst_EDRAMBlendRT01_Index) << (rt_index >> 1); shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(12)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0110, 1)); shader_code_.push_back(factor_calculation_temp); shader_code_.push_back(EncodeVectorReplicatedOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, (rt_index & 1) * 2, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_EDRAMBlendRT01_Vec + (rt_index >> 1)); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(kBlendX_Src_SrcAlphaSaturate); shader_code_.push_back(kBlendX_Dest_SrcAlphaSaturate); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.uint_instruction_count; // 5) Replace the color factors with the saturated alpha. for (uint32_t i = 0; i < 2; ++i) { shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(i ? dest_factor_and_minmax_temp : src_factor_and_result_temp); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1 + i, 1)); shader_code_.push_back(factor_calculation_temp); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(factor_calculation_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(i ? dest_factor_and_minmax_temp : src_factor_and_result_temp); ++stat_.instruction_count; ++stat_.movc_instruction_count; } // Multiply the colors by the factors. for (uint32_t i = 0; i < 2; ++i) { uint32_t factor_temp = i ? dest_factor_and_minmax_temp : src_factor_and_result_temp; uint32_t color_temp = i ? dest_color_temp : src_color_and_output_temp; // Check if the factor is zero to zero the result later. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_EQ) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(factor_calculation_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(factor_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.float_instruction_count; // Multiply. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MUL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(factor_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(color_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(factor_temp); ++stat_.instruction_count; ++stat_.float_instruction_count; // Zero the result if the factor was zero (and the color could be Infinity). shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(12)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(factor_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(factor_calculation_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(factor_temp); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Check if the color is zero. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_EQ) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(factor_calculation_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(color_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.float_instruction_count; // Zero the result if the color was zero (and the factor could be Infinity). shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(12)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(factor_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(factor_calculation_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(factor_temp); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Clamp the color if the components aren't floating-point. // https://docs.microsoft.com/en-us/windows/desktop/direct3d11/d3d10-graphics-programming-guide-output-merger-stage CompletePixelShader_WriteToROV_ClampColor(rt_index, factor_temp, factor_calculation_temp); shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(factor_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kROVRTFormatFlagTemp_Fixed_Swizzle, 1)); shader_code_.push_back(rt_format_flags_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(factor_calculation_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(factor_temp); ++stat_.instruction_count; ++stat_.movc_instruction_count; } // Apply the signs for addition/subtraction/inverse subtraction and // add/subtract/inverse subtract (for min/max, this will be overwritten // later). CompletePixelShader_WriteToROV_ExtractBlendScales( rt_index, 0b01010101, true, kBlendY_Src_OpSign_Shift, kBlendY_SrcAlpha_OpSign_Shift, kBlendY_Dest_OpSign_Shift, kBlendY_DestAlpha_OpSign_Shift, scale_temp); // 1) Apply the source signs (zero is not used, so no need to check). shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MUL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(src_factor_and_result_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(src_factor_and_result_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b01000000, 1)); shader_code_.push_back(scale_temp); ++stat_.instruction_count; ++stat_.float_instruction_count; // 2) Apply the destination signs and combine. dest_factor_and_minmax_temp // may be reused for min/max from now on. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(src_factor_and_result_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(dest_factor_and_minmax_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b11101010, 1)); shader_code_.push_back(scale_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(src_factor_and_result_temp); ++stat_.instruction_count; ++stat_.float_instruction_count; // Extract whether min/max should be done. system_constants_used_ |= (1ull << kSysConst_EDRAMBlendRT01_Index) << (rt_index >> 1); shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(12)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(scale_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, (rt_index & 1) ? 0b11111111 : 0b01010101, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_EDRAMBlendRT01_Vec + (rt_index >> 1)); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(kBlendY_Color_OpMin); shader_code_.push_back(kBlendY_Alpha_OpMin); shader_code_.push_back(kBlendY_Color_OpMax); shader_code_.push_back(kBlendY_Alpha_OpMax); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Replace the result with the minimum or the maximum of the source and the // destination because min/max don't use factors (also not using anything // involving multiplication for this so 0 * Infinity may not affect this). // Final output to src_color_and_output_temp happens here. for (uint32_t i = 0; i < 2; ++i) { shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE( i ? D3D10_SB_OPCODE_MAX : D3D10_SB_OPCODE_MIN) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(dest_factor_and_minmax_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(src_color_and_output_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(dest_color_temp); ++stat_.instruction_count; ++stat_.float_instruction_count; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); // In min, writing to the intermediate result register because max still // needs the original source color. // In max, doing the final output. shader_code_.push_back(i ? src_color_and_output_temp : src_factor_and_result_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, i ? 0b11101010 : 0b01000000, 1)); shader_code_.push_back(scale_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(dest_factor_and_minmax_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(src_factor_and_result_temp); ++stat_.instruction_count; ++stat_.movc_instruction_count; } // Release scale_temp, factor_calculation_temp, src_factor_and_result_temp // and dest_factor_and_minmax_temp. PopSystemTemp(4); } void DxbcShaderTranslator::CompletePixelShader_WriteToROV_StoreColor( uint32_t edram_dword_offset_low_temp, uint32_t edram_dword_offset_high_temp, uint32_t rt_index, uint32_t rt_format_flags_temp, uint32_t source_and_scratch_temp) { // For indexing of the format constants. uint32_t rt_pair_index = rt_index >> 1; uint32_t rt_pair_swizzle = rt_index & 1 ? 0b11101010 : 0b01000000; // Scale by the fixed-point conversion factor. system_constants_used_ |= (1ull << kSysConst_EDRAMStoreScaleRT01_Index) << rt_pair_index; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MUL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(source_and_scratch_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(source_and_scratch_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, rt_pair_swizzle, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_EDRAMStoreScaleRT01_Vec + rt_pair_index); ++stat_.instruction_count; ++stat_.float_instruction_count; // Convert to fixed-point, rounding to the nearest integer. // https://docs.microsoft.com/en-us/windows/desktop/direct3d10/d3d10-graphics-programming-guide-resources-data-conversion uint32_t fixed_temp = PushSystemTemp(); shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ROUND_NE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(fixed_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(source_and_scratch_temp); ++stat_.instruction_count; ++stat_.float_instruction_count; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_FTOI) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(fixed_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(fixed_temp); ++stat_.instruction_count; ++stat_.conversion_instruction_count; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(source_and_scratch_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kROVRTFormatFlagTemp_Fixed_Swizzle, 1)); shader_code_.push_back(rt_format_flags_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(fixed_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(source_and_scratch_temp); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Release fixed_temp. PopSystemTemp(); // *************************************************************************** // 7e3 conversion begins here. // https://github.com/Microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexConvert.cpp // *************************************************************************** // Check if the target format is 7e3 and the conversion is needed (this is // pretty long, better to branch here). shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IF) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN( D3D10_SB_INSTRUCTION_TEST_NONZERO) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_TEMP, kROVRTFormatFlagTemp_Float10, 1)); shader_code_.push_back(rt_format_flags_temp); ++stat_.instruction_count; ++stat_.dynamic_flow_control_count; uint32_t f10_temp1 = PushSystemTemp(), f10_temp2 = PushSystemTemp(); // Assuming RGB is already clamped to [0.0, 31.875], and alpha is a float and // already clamped and multiplied by 3 to get [0.0, 3.0]. // Calculate the denormalized value if the numbers are too small to be // represented as normalized 7e3 into f10_temp1. // t1 = f32 & 0x7FFFFF shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(f10_temp1); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(source_and_scratch_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0x7FFFFF); shader_code_.push_back(0x7FFFFF); shader_code_.push_back(0x7FFFFF); shader_code_.push_back(0x7FFFFF); ++stat_.instruction_count; ++stat_.uint_instruction_count; // t1 = (f32 & 0x7FFFFF) | 0x800000 shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_OR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(f10_temp1); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(f10_temp1); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0x800000); shader_code_.push_back(0x800000); shader_code_.push_back(0x800000); shader_code_.push_back(0x800000); ++stat_.instruction_count; ++stat_.uint_instruction_count; // t2 = f32 >> 23 shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_USHR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(f10_temp2); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(source_and_scratch_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(23); shader_code_.push_back(23); shader_code_.push_back(23); shader_code_.push_back(23); ++stat_.instruction_count; ++stat_.uint_instruction_count; // t2 = 125 - (f32 >> 23) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(11)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(f10_temp2); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(125); shader_code_.push_back(125); shader_code_.push_back(125); shader_code_.push_back(125); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1) | ENCODE_D3D10_SB_OPERAND_EXTENDED(1)); shader_code_.push_back( ENCODE_D3D10_SB_EXTENDED_OPERAND_MODIFIER(D3D10_SB_OPERAND_MODIFIER_NEG)); shader_code_.push_back(f10_temp2); ++stat_.instruction_count; ++stat_.int_instruction_count; // t1 = ((f32 & 0x7FFFFF) | 0x800000) >> (125 - (f32 >> 23)) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_USHR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(f10_temp1); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(f10_temp1); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(f10_temp2); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Check if the numbers are too small to be represented as normalized 7e3. // t2 = f32 < 0x3E800000 shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ULT) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(f10_temp2); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(source_and_scratch_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0x3E800000); shader_code_.push_back(0x3E800000); shader_code_.push_back(0x3E800000); shader_code_.push_back(0x3E800000); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Bias the exponent. // f32 += 0xC2000000 shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(source_and_scratch_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(source_and_scratch_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0xC2000000u); shader_code_.push_back(0xC2000000u); shader_code_.push_back(0xC2000000u); shader_code_.push_back(0xC2000000u); ++stat_.instruction_count; ++stat_.int_instruction_count; // Replace the number in f32 with a denormalized one if needed. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(source_and_scratch_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(f10_temp2); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(f10_temp1); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(source_and_scratch_temp); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Build the 7e3 numbers. // t1 = f32 >> 16 shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_USHR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(f10_temp1); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(source_and_scratch_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(16); shader_code_.push_back(16); shader_code_.push_back(16); shader_code_.push_back(16); ++stat_.instruction_count; ++stat_.uint_instruction_count; // t1 = (f32 >> 16) & 1 shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(f10_temp1); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(f10_temp1); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(1); shader_code_.push_back(1); shader_code_.push_back(1); shader_code_.push_back(1); ++stat_.instruction_count; ++stat_.uint_instruction_count; // f10 = f32 + 0x7FFF shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(source_and_scratch_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(source_and_scratch_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0x7FFF); shader_code_.push_back(0x7FFF); shader_code_.push_back(0x7FFF); shader_code_.push_back(0x7FFF); ++stat_.instruction_count; ++stat_.int_instruction_count; // f10 = f32 + 0x7FFF + ((f32 >> 16) & 1) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(source_and_scratch_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(source_and_scratch_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(f10_temp1); ++stat_.instruction_count; ++stat_.int_instruction_count; // f10 = (f32 + 0x7FFF + ((f32 >> 16) & 1)) >> 16 shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_USHR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(source_and_scratch_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(source_and_scratch_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(16); shader_code_.push_back(16); shader_code_.push_back(16); shader_code_.push_back(16); ++stat_.instruction_count; ++stat_.uint_instruction_count; // f10 = ((f32 + 0x7FFF + ((f32 >> 16) & 1)) >> 16) & 0x3FF shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(source_and_scratch_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(source_and_scratch_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0x3FF); shader_code_.push_back(0x3FF); shader_code_.push_back(0x3FF); shader_code_.push_back(0x3FF); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Release f10_temp1 and f10_temp2. PopSystemTemp(2); // 7e3 conversion done. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; // *************************************************************************** // 7e3 conversion ends here. // *************************************************************************** // Convert to 16-bit float. uint32_t f16_temp = PushSystemTemp(); shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_F32TOF16) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(f16_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(source_and_scratch_temp); ++stat_.instruction_count; ++stat_.conversion_instruction_count; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(source_and_scratch_temp); shader_code_.push_back(EncodeVectorReplicatedOperand( D3D10_SB_OPERAND_TYPE_TEMP, kROVRTFormatFlagTemp_Float16, 1)); shader_code_.push_back(rt_format_flags_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(f16_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(source_and_scratch_temp); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Release f16_temp. PopSystemTemp(); // Pack and store the lower and the upper 32 bits. uint32_t pack_temp = PushSystemTemp(); uint32_t pack_width_temp = PushSystemTemp(); uint32_t pack_offset_temp = PushSystemTemp(); for (uint32_t i = 0; i < 2; ++i) { if (i != 0) { // Check if need to store the upper 32 bits. system_constants_used_ |= 1ull << kSysConst_EDRAMRTPackWidthHigh_Index; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IF) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN( D3D10_SB_INSTRUCTION_TEST_NONZERO) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, rt_index, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_EDRAMRTPackWidthHigh_Vec); ++stat_.instruction_count; ++stat_.dynamic_flow_control_count; } // Insert color components into different vector components. CompletePixelShader_WriteToROV_ExtractPackLayout( rt_index, i != 0, pack_width_temp, pack_offset_temp); shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_BFI) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(14)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(pack_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(pack_width_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(pack_offset_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(source_and_scratch_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.uint_instruction_count; // bfi doesn't work with width 32 - handle it specially. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_USHR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1 << i, 1)); shader_code_.push_back(pack_width_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, i, 1)); shader_code_.push_back(pack_width_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(5); ++stat_.instruction_count; ++stat_.uint_instruction_count; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1 << i, 1)); shader_code_.push_back(pack_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, i, 1)); shader_code_.push_back(pack_width_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, i, 1)); shader_code_.push_back(source_and_scratch_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, i, 1)); shader_code_.push_back(pack_temp); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Merge XY and ZW. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_OR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0011, 1)); shader_code_.push_back(pack_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(pack_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b01001110, 1)); shader_code_.push_back(pack_temp); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Merge X and Y. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_OR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(pack_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(pack_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(pack_temp); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Write the dword. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_STORE_UAV_TYPED) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(8)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D11_SB_OPERAND_TYPE_UNORDERED_ACCESS_VIEW, 0b1111, 2)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, rt_index, 1)); shader_code_.push_back(i ? edram_dword_offset_high_temp : edram_dword_offset_low_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(pack_temp); ++stat_.instruction_count; ++stat_.c_texture_store_instructions; if (i != 0) { // Upper 32 bits stored. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; } } // Release pack_temp, pack_width_temp, pack_offset_temp. PopSystemTemp(3); } void DxbcShaderTranslator::CompletePixelShader_WriteToROV() { bool color_targets_written; if (is_depth_only_pixel_shader_) { color_targets_written = false; } else { color_targets_written = writes_color_target(0) || writes_color_target(1) || writes_color_target(2) || writes_color_target(3); } // *************************************************************************** // Calculate the offsets of the samples in the EDRAM. // *************************************************************************** uint32_t edram_coord_low_temp = PushSystemTemp(); // Load SV_Position in edram_coord_low_temp.xy as an integer. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_FTOU) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0011, 1)); shader_code_.push_back(edram_coord_low_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_INPUT, kSwizzleXYZW, 1)); shader_code_.push_back(uint32_t(InOutRegister::kPSInPosition)); ++stat_.instruction_count; ++stat_.conversion_instruction_count; // Load X tile index to edram_coord_low_temp.z, part 1 of the division by 80 - // get the high 32 bits of the result of the multiplication by 0xCCCCCCCD. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_UMUL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(8)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1)); shader_code_.push_back(edram_coord_low_temp); shader_code_.push_back( ENCODE_D3D10_SB_OPERAND_NUM_COMPONENTS(D3D10_SB_OPERAND_0_COMPONENT) | ENCODE_D3D10_SB_OPERAND_TYPE(D3D10_SB_OPERAND_TYPE_NULL) | ENCODE_D3D10_SB_OPERAND_INDEX_DIMENSION(D3D10_SB_OPERAND_INDEX_0D)); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(edram_coord_low_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0xCCCCCCCDu); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Load tile index to edram_coord_low_temp.zw. Part 2 of the division by 80 - // right shift the high bits of x*0xCCCCCCCD by 6. And divide by 16 by right // shifting by 4. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_USHR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1100, 1)); shader_code_.push_back(edram_coord_low_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b01100100, 1)); shader_code_.push_back(edram_coord_low_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(6); shader_code_.push_back(4); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Write tile-relative offset in XY. Subtract the tile index * 80x16 from the // position. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IMAD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(12)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0011, 1)); shader_code_.push_back(edram_coord_low_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b11101110, 1)); shader_code_.push_back(edram_coord_low_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(uint32_t(-80)); shader_code_.push_back(uint32_t(-16)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(edram_coord_low_temp); ++stat_.instruction_count; ++stat_.int_instruction_count; // Multiply tile Y index by the pitch and add X tile index to it to // edram_coord_low_temp.z. system_constants_used_ |= 1ull << kSysConst_EDRAMPitchTiles_Index; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_UMAD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(11)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1)); shader_code_.push_back(edram_coord_low_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(edram_coord_low_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSysConst_EDRAMPitchTiles_Comp, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_EDRAMPitchTiles_Vec); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(edram_coord_low_temp); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Swap 40 sample columns within the tile for the depth buffer into // system_temp_depth_.w - shaders uploading depth to the EDRAM by aliasing a // color render target expect this. // 1) Check in which half of the tile the sample is. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ULT) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1000, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(edram_coord_low_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(40); ++stat_.instruction_count; ++stat_.uint_instruction_count; // 2) Get the value to add to the tile-relative X sample index. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1000, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(40); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(uint32_t(-40)); ++stat_.instruction_count; ++stat_.movc_instruction_count; // 3) Actually swap the 40 sample columns. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1000, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(edram_coord_low_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(system_temp_depth_); ++stat_.instruction_count; ++stat_.int_instruction_count; // Calculate the address in the EDRAM buffer. if (color_targets_written) { // 1a) Get dword offset within the tile to edram_coord_low_temp.x. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_UMAD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(edram_coord_low_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(edram_coord_low_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(80); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(edram_coord_low_temp); ++stat_.instruction_count; ++stat_.uint_instruction_count; } // 1b) Do the same for depth/stencil to system_temp_depth_.w. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_UMAD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1000, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(edram_coord_low_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(80); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(system_temp_depth_); ++stat_.instruction_count; ++stat_.uint_instruction_count; if (color_targets_written) { // 2a) Combine the tile offset and the offset within the tile to // edram_coord_low_temp.x. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_UMAD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(edram_coord_low_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(edram_coord_low_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(1280); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(edram_coord_low_temp); ++stat_.instruction_count; ++stat_.uint_instruction_count; } // 2b) Do the same for depth/stencil to system_temp_depth_.w. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_UMAD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1000, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(edram_coord_low_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(1280); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(system_temp_depth_); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Adjust the offsets for 64 bits per pixel, and add EDRAM bases of color // render targets. uint32_t edram_coord_high_temp = 0; if (color_targets_written) { edram_coord_high_temp = PushSystemTemp(); // Get which render targets are 64bpp, as log2 of dword count per pixel. system_constants_used_ |= 1ull << kSysConst_EDRAMRTPackWidthHigh_Index; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(17)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(edram_coord_high_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSwizzleXYZW, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_EDRAMRTPackWidthHigh_Vec); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(1); shader_code_.push_back(1); shader_code_.push_back(1); shader_code_.push_back(1); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Multiply the offsets by 1 or 2 depending on the number of bits per pixel. // It's okay to do this here because everything in the equation (at least // for Xenia's representation of the EDRAM - may not be true on the real // console) needs to be multiplied by 2 - Y tile index (the same as // multipying the pitch by 2), X tile index (it addresses pairs of tiles in // this case), and the offset within a pair of tiles. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ISHL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(edram_coord_low_temp); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(edram_coord_low_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(edram_coord_high_temp); ++stat_.instruction_count; ++stat_.int_instruction_count; // Add the EDRAM bases for each render target. system_constants_used_ |= 1ull << kSysConst_EDRAMBaseDwords_Index; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(edram_coord_low_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(edram_coord_low_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSwizzleXYZW, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_EDRAMBaseDwords_Vec); ++stat_.instruction_count; ++stat_.int_instruction_count; // Get the offsets of the upper 32 bits. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(edram_coord_high_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(edram_coord_low_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(edram_coord_high_temp); ++stat_.instruction_count; ++stat_.int_instruction_count; } // Add the EDRAM base for depth. system_constants_used_ |= 1ull << kSysConst_EDRAMDepthBaseDwords_Index; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1000, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSysConst_EDRAMDepthBaseDwords_Comp, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_EDRAMDepthBaseDwords_Vec); ++stat_.instruction_count; ++stat_.int_instruction_count; // *************************************************************************** // Do depth/stencil testing. This must be done before the color writing, so // discard happens before the write, and also because in case the EDRAM base // of the depth buffer is for some reason the same as of some color buffer, // the color buffer should win - otherwise the Xbox Live Arcade splash screen // is missing in Banjo-Kazooie. // TODO(Triang3l): Do depth/stencil before the translated shader if possible. // *************************************************************************** uint32_t depth_stencil_test_temp = PushSystemTemp(); system_constants_used_ |= 1ull << kSysConst_Flags_Index; // Check if anything related to depth/stencil needs to be done at all, and get // the conditions of passing the depth test - as 0 or 0xFFFFFFFF - into // depth_stencil_test_temp. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_IBFE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(17)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(depth_stencil_test_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(1); shader_code_.push_back(1); shader_code_.push_back(1); shader_code_.push_back(1); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(kSysFlag_DepthStencil_Shift); shader_code_.push_back(kSysFlag_DepthPassIfLess_Shift); shader_code_.push_back(kSysFlag_DepthPassIfEqual_Shift); shader_code_.push_back(kSysFlag_DepthPassIfGreater_Shift); shader_code_.push_back(EncodeVectorReplicatedOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSysConst_Flags_Comp, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_Flags_Vec); ++stat_.instruction_count; ++stat_.int_instruction_count; // Enter the depth/stencil test if needed. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IF) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN( D3D10_SB_INSTRUCTION_TEST_NONZERO) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(depth_stencil_test_temp); ++stat_.instruction_count; ++stat_.dynamic_flow_control_count; // Convert the depth to the target format - won't modify the W value. No need // to do this in an if - if the value is not needed, the command processor can // specify that the format is unorm24 - the conversion is much easier this way // than for float24, only 2 instructions. CompletePixelShader_DepthTo24Bit(); // Load the previous combined depth/stencil value into system_temp_depth_.y. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_LD_UAV_TYPED) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(8)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back(EncodeVectorReplicatedOperand( D3D11_SB_OPERAND_TYPE_UNORDERED_ACCESS_VIEW, 0, 2)); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.texture_load_instructions; // Separate the previous depth/stencil into depth and stencil to // system_temp_depth_.yz. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_UBFE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(15)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0110, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(24); shader_code_.push_back(8); shader_code_.push_back(0); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(8); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(system_temp_depth_); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Allocate a register for depth/stencil testing. uint32_t depth_stencil_test_result_temp = PushSystemTemp(); // First, the depth test. // New depth in system_temp_depth_.x, old depth in system_temp_depth_.y. // 1) Equal. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IEQ) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1)); shader_code_.push_back(depth_stencil_test_result_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(system_temp_depth_); ++stat_.instruction_count; ++stat_.int_instruction_count; // 2) Less/greater. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ULT) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1010, 1)); shader_code_.push_back(depth_stencil_test_result_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b01000000, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b00000100, 1)); shader_code_.push_back(system_temp_depth_); ++stat_.instruction_count; ++stat_.uint_instruction_count; // 3) Compare the results with the expected. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1110, 1)); shader_code_.push_back(depth_stencil_test_result_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(depth_stencil_test_result_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(depth_stencil_test_temp); ++stat_.instruction_count; ++stat_.uint_instruction_count; // 4) Start combining the results into depth_stencil_test_result_temp.x. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_OR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(depth_stencil_test_result_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(depth_stencil_test_result_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(depth_stencil_test_result_temp); ++stat_.instruction_count; ++stat_.uint_instruction_count; // 5) Finish combining the results into depth_stencil_test_result_temp.x. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_OR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(depth_stencil_test_result_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(depth_stencil_test_result_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(depth_stencil_test_result_temp); ++stat_.instruction_count; ++stat_.uint_instruction_count; // depth_stencil_test_temp may be reused now. // Depth test done. Now check the depth write mask (write it to // depth_stencil_test_temp.x) and whether stencil testing is enabled (to // depth_stencil_test_temp.y). shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(12)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0011, 1)); shader_code_.push_back(depth_stencil_test_temp); shader_code_.push_back(EncodeVectorReplicatedOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSysConst_Flags_Comp, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_Flags_Vec); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(kSysFlag_DepthWriteMask); shader_code_.push_back(kSysFlag_StencilTest); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Preserve the original depth if the depth write mask is disabled - copy // system_temp_depth_.y to system_temp_depth_.x if needed. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(depth_stencil_test_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(system_temp_depth_); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Check if stencil test needs to be done. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IF) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN( D3D10_SB_INSTRUCTION_TEST_NONZERO) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(depth_stencil_test_temp); ++stat_.instruction_count; ++stat_.dynamic_flow_control_count; // depth_stencil_test_temp may be reused now. // *************************************************************************** // Stencil test begins here. // // The resulting stencil will overwrite the original stencil in // system_temp_depth_.z. // *************************************************************************** // Load the operations and the comparison to depth_stencil_test_temp. system_constants_used_ |= (1ull << kSysConst_EDRAMStencilFront_Index) | (1ull << kSysConst_EDRAMStencilBack_Index); shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(13)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(depth_stencil_test_temp); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_INPUT, 0, 1)); shader_code_.push_back(uint32_t(InOutRegister::kPSInFrontFace)); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSwizzleXYZW, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_EDRAMStencilFront_Vec); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSwizzleXYZW, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_EDRAMStencilBack_Vec); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Load masked reference and original value into // depth_stencil_test_result_temp.yw for comparison. system_constants_used_ |= (1ull << kSysConst_EDRAMStencilReference_Index) | (1ull << kSysConst_EDRAMStencilReadMask_Vec); shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(11)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(depth_stencil_test_result_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSysConst_EDRAMStencilReference_Comp, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_EDRAMStencilReference_Vec); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSysConst_EDRAMStencilReadMask_Comp, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_EDRAMStencilReadMask_Vec); ++stat_.instruction_count; ++stat_.uint_instruction_count; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1000, 1)); shader_code_.push_back(depth_stencil_test_result_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSysConst_EDRAMStencilReadMask_Comp, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_EDRAMStencilReadMask_Vec); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Compare the stencil values into depth_stencil_test_result_temp.y - // depth_stencil_test_result_temp.x contains the depth test result. // 1) Equal - has only 1 result, so won't overwrite the original values in yw. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IEQ) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1)); shader_code_.push_back(depth_stencil_test_result_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(depth_stencil_test_result_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(depth_stencil_test_result_temp); ++stat_.instruction_count; ++stat_.int_instruction_count; // 2) Less/greater - compare yw into yw. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ULT) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1010, 1)); shader_code_.push_back(depth_stencil_test_result_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(depth_stencil_test_result_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b01101100, 1)); shader_code_.push_back(depth_stencil_test_result_temp); ++stat_.instruction_count; ++stat_.uint_instruction_count; // 3) Extract the comparison values. uint32_t stencil_comparison_temp = PushSystemTemp(); shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_IBFE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(15)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1110, 1)); shader_code_.push_back(stencil_comparison_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(1); shader_code_.push_back(1); shader_code_.push_back(1); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(1); shader_code_.push_back(2); shader_code_.push_back(EncodeVectorReplicatedOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSysConst_EDRAMStencilSide_Comparison_Comp, 1)); shader_code_.push_back(depth_stencil_test_temp); ++stat_.instruction_count; ++stat_.int_instruction_count; // 4) Compare the results with the expected. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1110, 1)); shader_code_.push_back(depth_stencil_test_result_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(depth_stencil_test_result_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(stencil_comparison_temp); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Release stencil_comparison_temp. PopSystemTemp(); // 5) Start combining the results into depth_stencil_test_result_temp.y. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_OR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(depth_stencil_test_result_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(depth_stencil_test_result_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(depth_stencil_test_result_temp); ++stat_.instruction_count; ++stat_.uint_instruction_count; // 6) Finish combining the results into depth_stencil_test_result_temp.y. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_OR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(depth_stencil_test_result_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(depth_stencil_test_result_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(depth_stencil_test_result_temp); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Write the needed stencil operation to depth_stencil_test_result_temp.z. // 1) Assuming the stencil test has passed, choose between the "pass" // operation and the "depth fail" operation. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1)); shader_code_.push_back(depth_stencil_test_result_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(depth_stencil_test_result_temp); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSysConst_EDRAMStencilSide_Pass_Comp, 1)); shader_code_.push_back(depth_stencil_test_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSysConst_EDRAMStencilSide_DepthFail_Comp, 1)); shader_code_.push_back(depth_stencil_test_temp); ++stat_.instruction_count; ++stat_.movc_instruction_count; // 2) If the stencil test has failed, use the "fail" operation. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1)); shader_code_.push_back(depth_stencil_test_result_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(depth_stencil_test_result_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(depth_stencil_test_result_temp); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSysConst_EDRAMStencilSide_Fail_Comp, 1)); shader_code_.push_back(depth_stencil_test_temp); ++stat_.instruction_count; ++stat_.movc_instruction_count; // depth_stencil_test_temp may be reused now. // We don't need separate depth and stencil test results anymore, so now we // can mark the pixel to be discarded if the stencil test has failed - by // setting that whole depth/stencil test has failed. The original depth value // will be restored in this case, and after writing the new stencil, the pixel // will be discarded. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(depth_stencil_test_result_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(depth_stencil_test_result_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(depth_stencil_test_result_temp); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Extract the sub-operations to depth_stencil_test_temp. // // Don't care about & 0xFF now, applying the write mask will drop the unused // bits. // // X - current value mask (keep/increment/decrement/invert vs. zero/replace) - // 0xFFFFFFFF if the original value is needed, 0 if it needs to be zeroed. // Y - value to add (increment/decrement) - 0, 1 or 0xFFFFFFFF (-1). // Z - saturate - 0xFFFFFFFF to clamp to 0-255 after addition, 0 otherwise. // W - invert mask - for XORing: 0xFFFFFFFF if need to invert, 0 otherwise. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_IBFE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(15)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(depth_stencil_test_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(1); shader_code_.push_back(2); shader_code_.push_back(1); shader_code_.push_back(1); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(kStencilOp_Flag_CurrentMask_Shift); shader_code_.push_back(kStencilOp_Flag_Add_Shift); shader_code_.push_back(kStencilOp_Flag_Saturate_Shift); shader_code_.push_back(kStencilOp_Flag_Invert_Shift); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(depth_stencil_test_result_temp); ++stat_.instruction_count; ++stat_.int_instruction_count; // Mask the original value into depth_stencil_test_temp.x. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(depth_stencil_test_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(depth_stencil_test_temp); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Increment or decrement the value in depth_stencil_test_temp.x. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(depth_stencil_test_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(depth_stencil_test_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(depth_stencil_test_temp); ++stat_.instruction_count; ++stat_.int_instruction_count; // Saturate the value in depth_stencil_test_temp.x if needed, using // depth_stencil_test_temp.y as an intermediate for min/max. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IMAX) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(depth_stencil_test_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(depth_stencil_test_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.int_instruction_count; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IMIN) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(depth_stencil_test_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(depth_stencil_test_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0xFF); ++stat_.instruction_count; ++stat_.int_instruction_count; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(depth_stencil_test_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(depth_stencil_test_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(depth_stencil_test_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(depth_stencil_test_temp); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Invert the value in depth_stencil_test_temp.x if needed. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_XOR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(depth_stencil_test_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(depth_stencil_test_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(depth_stencil_test_temp); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Replace the value in depth_stencil_test_temp.x with the reference if // needed. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(depth_stencil_test_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(depth_stencil_test_result_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(kStencilOp_Flag_NewMask); ++stat_.instruction_count; ++stat_.uint_instruction_count; system_constants_used_ |= 1ull << kSysConst_EDRAMStencilReference_Index; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(11)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(depth_stencil_test_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(depth_stencil_test_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSysConst_EDRAMStencilReference_Comp, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_EDRAMStencilReference_Vec); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(depth_stencil_test_temp); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Apply the write mask to the new value - this will also reduce it to 8 bits. system_constants_used_ |= 1ull << kSysConst_EDRAMStencilWriteMask_Index; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(depth_stencil_test_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(depth_stencil_test_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSysConst_EDRAMStencilWriteMask_Comp, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_EDRAMStencilWriteMask_Vec); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Invert the write mask to keep the unmodified bits of the old value. system_constants_used_ |= 1ull << kSysConst_EDRAMStencilWriteMask_Index; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_NOT) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(depth_stencil_test_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSysConst_EDRAMStencilWriteMask_Comp, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_EDRAMStencilWriteMask_Vec); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Mask the old value. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(depth_stencil_test_temp); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Combine the old and new stencil values. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_OR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(depth_stencil_test_temp); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Preserve the original depth value if either depth or stencil test has // failed because when stencil testing is enabled, something will be written // since stencil may be modified on failure too (if stencil is disabled, // there's a discard later, in the "else" case, that will prevent depth // modification too). shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(depth_stencil_test_result_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(system_temp_depth_); ++stat_.instruction_count; ++stat_.movc_instruction_count; // *************************************************************************** // Stencil test ends here. // *************************************************************************** // If not doing stencil test, it's safe to discard a bit earlier - no need to // modify the stencil, no need to modify the depth and to write to the memory. // Check if stencil test is not done. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ELSE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; // Discard the pixel if depth test failed and no stencil testing. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DISCARD) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN(D3D10_SB_INSTRUCTION_TEST_ZERO) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(depth_stencil_test_result_temp); ++stat_.instruction_count; // Stencil test done. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; // Get the bit to check if need to write the new depth/stencil value. // The write masks of depth specifically and stencil specifically are handled // before. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(depth_stencil_test_temp); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSysConst_Flags_Comp, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_Flags_Vec); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(kSysFlag_DepthStencilWrite); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Actually check if need to write the new depth/stencil value. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IF) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN( D3D10_SB_INSTRUCTION_TEST_NONZERO) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(depth_stencil_test_temp); ++stat_.instruction_count; ++stat_.dynamic_flow_control_count; // Combine depth (in system_temp_depth_.x) and stencil (in // system_temp_depth_.z - either new, if stencil test is enabled, or // unchanged, if it's disabled) into system_temp_depth_.x. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_UMAD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(256); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(system_temp_depth_); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Write the new depth/stencil value. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_STORE_UAV_TYPED) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(8)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D11_SB_OPERAND_TYPE_UNORDERED_ACCESS_VIEW, 0b1111, 2)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(system_temp_depth_); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_depth_); ++stat_.instruction_count; ++stat_.c_texture_store_instructions; // Depth/stencil writing done. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; // Discard the pixel if depth/stencil test failed. This needs to be done after // writing because stencil may be modified even if the depth/stencil test // fails. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DISCARD) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN(D3D10_SB_INSTRUCTION_TEST_ZERO) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(depth_stencil_test_result_temp); ++stat_.instruction_count; // Release depth_stencil_test_result_temp. PopSystemTemp(); // Depth/stencil operations done. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; // Release depth_stencil_test_temp. PopSystemTemp(); // *************************************************************************** // Write to color render targets. // *************************************************************************** if (color_targets_written) { system_constants_used_ |= 1ull << kSysConst_EDRAMRTFlags_Index; // Mask disabled color writes. uint32_t rt_write_masks_temp = PushSystemTemp(); shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_USHR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(12)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(rt_write_masks_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSwizzleXYZW, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_EDRAMRTFlags_Vec); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(kRTFlag_WriteR_Shift); shader_code_.push_back(kRTFlag_WriteR_Shift); shader_code_.push_back(kRTFlag_WriteR_Shift); shader_code_.push_back(kRTFlag_WriteR_Shift); ++stat_.instruction_count; ++stat_.uint_instruction_count; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(system_temp_color_written_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_color_written_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(rt_write_masks_temp); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Release rt_write_masks_temp. PopSystemTemp(); // Get what render targets need gamma conversion. uint32_t rt_gamma_temp = PushSystemTemp(); system_constants_used_ |= 1ull << kSysConst_Flags_Index; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(12)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(rt_gamma_temp); shader_code_.push_back(EncodeVectorReplicatedOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSysConst_Flags_Comp, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_Flags_Vec); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(kSysFlag_Color0Gamma); shader_code_.push_back(kSysFlag_Color1Gamma); shader_code_.push_back(kSysFlag_Color2Gamma); shader_code_.push_back(kSysFlag_Color3Gamma); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Get what render targets need blending (if only write mask is used and no // blending, skip blending). uint32_t rt_blend_temp = PushSystemTemp(); shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(12)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(rt_blend_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSwizzleXYZW, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_EDRAMRTFlags_Vec); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(kRTFlag_Blend); shader_code_.push_back(kRTFlag_Blend); shader_code_.push_back(kRTFlag_Blend); shader_code_.push_back(kRTFlag_Blend); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Get what render targets need to be read (for write mask and blending). uint32_t rt_overwritten_temp = PushSystemTemp(); // First, ignore components that don't exist in the render target at all - // treat them as overwritten. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_UBFE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(17)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(rt_overwritten_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(4); shader_code_.push_back(4); shader_code_.push_back(4); shader_code_.push_back(4); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(kRTFlag_FormatUnusedR_Shift); shader_code_.push_back(kRTFlag_FormatUnusedR_Shift); shader_code_.push_back(kRTFlag_FormatUnusedR_Shift); shader_code_.push_back(kRTFlag_FormatUnusedR_Shift); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSwizzleXYZW, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_EDRAMRTFlags_Vec); ++stat_.instruction_count; ++stat_.uint_instruction_count; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_OR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(rt_overwritten_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_color_written_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(rt_overwritten_temp); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Then, check if the write mask + unused components is 1111 - if yes (and // not blending), the pixel will be totally overwritten and no need to load // the old pixel value. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IEQ) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(rt_overwritten_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(rt_overwritten_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0b1111); shader_code_.push_back(0b1111); shader_code_.push_back(0b1111); shader_code_.push_back(0b1111); ++stat_.instruction_count; ++stat_.int_instruction_count; // Force load the previous pixel if blending. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(12)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(rt_overwritten_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(rt_blend_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(rt_overwritten_temp); ++stat_.instruction_count; ++stat_.movc_instruction_count; for (uint32_t i = 0; i < 4; ++i) { if (!writes_color_target(i)) { continue; } // Check if the render target needs to be written to. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IF) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN( D3D10_SB_INSTRUCTION_TEST_NONZERO) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, i, 1)); shader_code_.push_back(system_temp_color_written_); ++stat_.instruction_count; ++stat_.dynamic_flow_control_count; // Load the format flags: // X - color is fixed-point (kROVRTFormatFlagTemp_ColorFixed). // Y - alpha is fixed-point (kROVRTFormatFlagTemp_AlphaFixed). // Z - format is 2:10:10:10 floating-point (kROVRTFormatFlagTemp_Float10). // W - format is 16-bit floating-point (kROVRTFormatFlagTemp_Float16). uint32_t format_flags_temp = PushSystemTemp(); system_constants_used_ |= 1ull << kSysConst_EDRAMRTFlags_Index; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(12)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(format_flags_temp); shader_code_.push_back(EncodeVectorReplicatedOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, i, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_EDRAMRTFlags_Vec); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(kRTFlag_FormatFixed); shader_code_.push_back(kRTFlag_FormatFixed | kRTFlag_FormatFloat10); shader_code_.push_back(kRTFlag_FormatFloat10); shader_code_.push_back(kRTFlag_FormatFloat16); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Allocate temporary registers for loading the previous color and for the // write mask. This is done because some operations - clamping, gamma // correction - should be done only for the source color. If no need to // get the previous color, will just assume use the 1111 write mask for // the movc. uint32_t dest_color_temp = PushSystemTemp(); uint32_t write_mask_temp = PushSystemTemp(); // Check if need to load the previous value in the render target. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IF) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN( D3D10_SB_INSTRUCTION_TEST_ZERO) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, i, 1)); shader_code_.push_back(rt_overwritten_temp); ++stat_.instruction_count; ++stat_.dynamic_flow_control_count; // Load the previous value in the render target to blend and to apply the // write mask. CompletePixelShader_WriteToROV_LoadColor( edram_coord_low_temp, edram_coord_high_temp, i, format_flags_temp, dest_color_temp); // Blend if needed. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IF) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN( D3D10_SB_INSTRUCTION_TEST_NONZERO) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, i, 1)); shader_code_.push_back(rt_blend_temp); ++stat_.instruction_count; ++stat_.dynamic_flow_control_count; // Convert the destination to linear before blending - to an intermediate // register because write masking will use dest_color_temp too. // https://steamcdn-a.akamaihd.net/apps/valve/2008/GDC2008_PostProcessingInTheOrangeBox.pdf uint32_t dest_color_linear_temp = PushSystemTemp(); shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(dest_color_linear_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(dest_color_temp); ++stat_.instruction_count; ++stat_.mov_instruction_count; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IF) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN( D3D10_SB_INSTRUCTION_TEST_NONZERO) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, i, 1)); shader_code_.push_back(rt_gamma_temp); ++stat_.instruction_count; ++stat_.dynamic_flow_control_count; CompletePixelShader_GammaCorrect(dest_color_linear_temp, false); shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; CompletePixelShader_WriteToROV_Blend( i, format_flags_temp, system_temp_color_[i], dest_color_linear_temp); // Release dest_color_linear_temp. PopSystemTemp(); shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; // Mask the components to overwrite. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(write_mask_temp); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, i, 1)); shader_code_.push_back(system_temp_color_written_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(1 << 0); shader_code_.push_back(1 << 1); shader_code_.push_back(1 << 2); shader_code_.push_back(1 << 3); ++stat_.instruction_count; ++stat_.uint_instruction_count; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ELSE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; // If not using the previous color, set the write mask to 1111 to ignore // the uninitialized register with the previous color. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(8)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(write_mask_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(1); shader_code_.push_back(1); shader_code_.push_back(1); shader_code_.push_back(1); ++stat_.instruction_count; ++stat_.mov_instruction_count; shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; // Clamp to the representable range after blending (for float10 and // float16, clamping is not done during blending) and before storing. CompletePixelShader_WriteToROV_ClampColor(i, system_temp_color_[i], system_temp_color_[i]); // Convert to gamma space after blending. // https://steamcdn-a.akamaihd.net/apps/valve/2008/GDC2008_PostProcessingInTheOrangeBox.pdf shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IF) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN( D3D10_SB_INSTRUCTION_TEST_NONZERO) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, i, 1)); shader_code_.push_back(rt_gamma_temp); ++stat_.instruction_count; ++stat_.dynamic_flow_control_count; CompletePixelShader_GammaCorrect(system_temp_color_[i], true); shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; // Keep previous values of the components where needed. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(system_temp_color_[i]); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(write_mask_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_color_[i]); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(dest_color_temp); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Write the new color, which may have been modified by blending. CompletePixelShader_WriteToROV_StoreColor( edram_coord_low_temp, edram_coord_high_temp, i, format_flags_temp, system_temp_color_[i]); // Release format_flags_temp, dest_color_temp and write_mask_temp. PopSystemTemp(3); // Close the check whether the RT is used. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; } // Release rt_gamma_temp, rt_blend_temp and rt_overwritten_temp. PopSystemTemp(3); } // Release edram_coord_low_temp and, if used, edram_coord_high_temp. PopSystemTemp(color_targets_written ? 2 : 1); } 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; } // Alpha test. // Check if alpha test is enabled (if the constant is not 0). system_constants_used_ |= (1ull << kSysConst_AlphaTest_Index) | (1ull << kSysConst_AlphaTestRange_Index); shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IF) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN( D3D10_SB_INSTRUCTION_TEST_NONZERO) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSysConst_AlphaTest_Comp, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_AlphaTest_Vec); ++stat_.instruction_count; ++stat_.dynamic_flow_control_count; // Allocate a register for the test result. uint32_t alpha_test_reg = PushSystemTemp(); // Check the alpha against the lower bound (inclusively). shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_GE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(alpha_test_reg); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(system_temp_color_[0]); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSysConst_AlphaTestRange_Comp, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_AlphaTestRange_Vec); ++stat_.instruction_count; ++stat_.float_instruction_count; // Check the alpha against the upper bound (inclusively). shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_GE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(alpha_test_reg); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSysConst_AlphaTestRange_Comp + 1, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_AlphaTestRange_Vec); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(system_temp_color_[0]); ++stat_.instruction_count; ++stat_.float_instruction_count; // Check if both tests have passed and the alpha is in the range. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(alpha_test_reg); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(alpha_test_reg); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(alpha_test_reg); ++stat_.instruction_count; ++stat_.uint_instruction_count; // xe_alpha_test of 1 means alpha test passes in the range, -1 means it fails. // Compare xe_alpha_test to 0 and see what action should be performed. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ILT) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(alpha_test_reg); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSysConst_AlphaTest_Comp, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_AlphaTest_Vec); ++stat_.instruction_count; ++stat_.int_instruction_count; // Flip the test result if alpha being in the range means passing. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_XOR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(alpha_test_reg); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(alpha_test_reg); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(alpha_test_reg); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Discard the texel if failed the test. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DISCARD) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN( D3D10_SB_INSTRUCTION_TEST_NONZERO) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(alpha_test_reg); ++stat_.instruction_count; // Release alpha_test_reg. PopSystemTemp(); // Close the alpha test conditional. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; // Apply color exponent bias (the constant contains 2.0^bias). // Not sure if this should be done before alpha testing or after, but this is // render target state, and alpha test works with values obtained mainly from // textures (so conceptually closer to the shader rather than the // output-merger in the pipeline). // TODO(Triang3l): Verify whether the order of alpha testing and exponent bias // is correct. system_constants_used_ |= 1ull << kSysConst_ColorExpBias_Index; for (uint32_t i = 0; i < 4; ++i) { shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MUL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(system_temp_color_[i]); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_color_[i]); shader_code_.push_back(EncodeVectorReplicatedOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, i, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_ColorExpBias_Vec); ++stat_.instruction_count; ++stat_.float_instruction_count; } // Write the values to the render targets. if (edram_rov_used_) { CompletePixelShader_WriteToROV(); } else { CompletePixelShader_WriteToRTVs(); } } void DxbcShaderTranslator::CompleteShaderCode() { if (!is_depth_only_pixel_shader_) { // Close the last exec, there's nothing to merge it with anymore, and we're // closing upper-level flow control blocks. CloseExecConditionals(); // Close the last label and the switch. if (FLAGS_dxbc_switch) { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_BREAK) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDSWITCH) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; } else { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; } // End the main loop. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_BREAK) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDLOOP) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; // Release the following system temporary values so epilogue can reuse them: // - system_temp_pv_. // - system_temp_ps_pc_p0_a0_. // - system_temp_aL_. // - system_temp_loop_count_. // - system_temp_grad_h_lod_. // - system_temp_grad_v_. PopSystemTemp(6); } // Write stage-specific epilogue. if (IsDXBCVertexShader()) { CompleteVertexShader(); } else if (IsDXBCPixelShader()) { CompletePixelShader(); } if (IsDXBCVertexShader()) { // Release system_temp_position_. PopSystemTemp(); } else if (IsDXBCPixelShader()) { if (edram_rov_used_) { // Release system_temp_depth_. PopSystemTemp(); if (!is_depth_only_pixel_shader_) { // Release system_temp_color_written_. PopSystemTemp(); } } if (!is_depth_only_pixel_shader_) { // Release system_temp_color_. PopSystemTemp(4); } } // Return from `main`. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_RET) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; ++stat_.static_flow_control_count; // Remap float constant indices if not indexed dynamically. if (!float_constants_dynamic_indexed_ && !float_constant_index_offsets_.empty()) { uint8_t float_constant_map[256] = {}; uint32_t float_constant_count = 0; for (uint32_t i = 0; i < 4; ++i) { uint64_t float_constants_used = constant_register_map().float_bitmap[i]; uint32_t float_constant_index; while ( xe::bit_scan_forward(float_constants_used, &float_constant_index)) { float_constants_used &= ~(1ull << float_constant_index); float_constant_map[i * 64 + float_constant_index] = float_constant_count++; } } size_t index_count = float_constant_index_offsets_.size(); for (size_t i = 0; i < index_count; ++i) { uint32_t index_offset = float_constant_index_offsets_[i]; shader_code_[index_offset] = float_constant_map[shader_code_[index_offset] & 255]; } } } std::vector DxbcShaderTranslator::CompleteTranslation() { // Write the code epilogue. CompleteShaderCode(); shader_object_.clear(); // Write the shader object header. shader_object_.push_back('CBXD'); // Checksum (set later). for (uint32_t i = 0; i < 4; ++i) { shader_object_.push_back(0); } shader_object_.push_back(1); // Size (set later). shader_object_.push_back(0); // 5 chunks - RDEF, ISGN, OSGN, SHEX, STAT. shader_object_.push_back(5); // Chunk offsets (set later). for (uint32_t i = 0; i < shader_object_[7]; ++i) { shader_object_.push_back(0); } uint32_t chunk_position_dwords; // Write Resource DEFinitions. chunk_position_dwords = uint32_t(shader_object_.size()); shader_object_[8] = chunk_position_dwords * sizeof(uint32_t); shader_object_.push_back('FEDR'); shader_object_.push_back(0); WriteResourceDefinitions(); shader_object_[chunk_position_dwords + 1] = (uint32_t(shader_object_.size()) - chunk_position_dwords - 2) * sizeof(uint32_t); // Write Input SiGNature. chunk_position_dwords = uint32_t(shader_object_.size()); shader_object_[9] = chunk_position_dwords * sizeof(uint32_t); shader_object_.push_back('NGSI'); shader_object_.push_back(0); WriteInputSignature(); shader_object_[chunk_position_dwords + 1] = (uint32_t(shader_object_.size()) - chunk_position_dwords - 2) * sizeof(uint32_t); // Write Output SiGNature. chunk_position_dwords = uint32_t(shader_object_.size()); shader_object_[10] = chunk_position_dwords * sizeof(uint32_t); shader_object_.push_back('NGSO'); shader_object_.push_back(0); WriteOutputSignature(); shader_object_[chunk_position_dwords + 1] = (uint32_t(shader_object_.size()) - chunk_position_dwords - 2) * sizeof(uint32_t); // Write SHader EXtended. chunk_position_dwords = uint32_t(shader_object_.size()); shader_object_[11] = chunk_position_dwords * sizeof(uint32_t); shader_object_.push_back('XEHS'); shader_object_.push_back(0); WriteShaderCode(); shader_object_[chunk_position_dwords + 1] = (uint32_t(shader_object_.size()) - chunk_position_dwords - 2) * sizeof(uint32_t); // Write STATistics. chunk_position_dwords = uint32_t(shader_object_.size()); shader_object_[12] = chunk_position_dwords * sizeof(uint32_t); shader_object_.push_back('TATS'); shader_object_.push_back(sizeof(stat_)); shader_object_.resize(shader_object_.size() + sizeof(stat_) / sizeof(uint32_t)); std::memcpy(&shader_object_[chunk_position_dwords + 2], &stat_, sizeof(stat_)); // Fill the remaining fields of the header and copy bytes out. uint32_t shader_object_size = uint32_t(shader_object_.size() * sizeof(uint32_t)); shader_object_[6] = shader_object_size; // The checksum includes the size field, so it must be the last. CalculateDXBCChecksum(reinterpret_cast(shader_object_.data()), shader_object_size, reinterpret_cast(&shader_object_[1])); // TODO(Triang3l): Avoid copy? std::vector shader_object_bytes; shader_object_bytes.resize(shader_object_size); std::memcpy(shader_object_bytes.data(), shader_object_.data(), shader_object_size); return shader_object_bytes; } void DxbcShaderTranslator::EmitInstructionDisassembly() { if (!FLAGS_dxbc_source_map) { return; } const char* source = instruction_disassembly_buffer_.GetString(); uint32_t length = uint32_t(instruction_disassembly_buffer_.length()); // Trim leading spaces and trailing new line. while (length != 0 && source[0] == ' ') { ++source; --length; } while (length != 0 && source[length - 1] == '\n') { --length; } if (length == 0) { return; } uint32_t length_dwords = (length + 1 + (sizeof(uint32_t) - 1)) / sizeof(uint32_t); shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_CUSTOMDATA) | ENCODE_D3D10_SB_CUSTOMDATA_CLASS(D3D10_SB_CUSTOMDATA_COMMENT)); shader_code_.push_back(2 + length_dwords); size_t offset_dwords = shader_code_.size(); shader_code_.resize(offset_dwords + length_dwords); char* target = reinterpret_cast(&shader_code_[offset_dwords]); std::memcpy(target, source, length); target[length] = '\0'; // Don't leave uninitialized data, and make sure multiple invocations of the // translator for the same Xenos shader give the same DXBC. std::memset(target + length + 1, 0xAB, length_dwords * sizeof(uint32_t) - length - 1); } void DxbcShaderTranslator::LoadDxbcSourceOperand( const InstructionOperand& operand, DxbcSourceOperand& dxbc_operand) { // Initialize the values to their defaults. dxbc_operand.type = DxbcSourceOperand::Type::kZerosOnes; dxbc_operand.index = 0; dxbc_operand.addressing_mode = InstructionStorageAddressingMode::kStatic; dxbc_operand.swizzle = kSwizzleXYZW; dxbc_operand.is_negated = operand.is_negated; dxbc_operand.is_absolute_value = operand.is_absolute_value; dxbc_operand.intermediate_register = DxbcSourceOperand::kIntermediateRegisterNone; if (operand.component_count == 0) { // No components requested, probably totally invalid - give something more // or less safe (zeros) and exit. assert_always(); return; } // Make the DXBC swizzle, and also check whether there are any components with // constant zero or one values (in this case, the operand will have to be // loaded into the intermediate register) and if there are any real components // at all (if there aren't, a literal can just be loaded). uint32_t swizzle = 0; uint32_t constant_components = 0; uint32_t constant_component_values = 0; for (uint32_t i = 0; i < uint32_t(operand.component_count); ++i) { if (operand.components[i] <= SwizzleSource::kW) { swizzle |= uint32_t(operand.components[i]) << (2 * i); } else { constant_components |= 1 << i; if (operand.components[i] == SwizzleSource::k1) { constant_component_values |= 1 << i; } } } // Replicate the last component's swizzle into all unused components. uint32_t component_last = uint32_t(operand.component_count) - 1; for (uint32_t i = uint32_t(operand.component_count); i < 4; ++i) { swizzle |= ((swizzle >> (2 * component_last)) & 0x3) << (2 * i); constant_components |= ((constant_components >> component_last) & 0x1) << i; constant_component_values |= ((constant_component_values >> component_last) & 0x1) << i; } // If all components are constant, just write a literal. if (constant_components == 0xF) { dxbc_operand.index = constant_component_values; return; } dxbc_operand.swizzle = swizzle; // If the index is dynamic, choose where it's taken from. uint32_t dynamic_address_register, dynamic_address_component; if (operand.storage_addressing_mode == InstructionStorageAddressingMode::kAddressRelative) { // Addressed by aL.x. dynamic_address_register = system_temp_aL_; dynamic_address_component = 0; } else { // Addressed by a0. dynamic_address_register = system_temp_ps_pc_p0_a0_; dynamic_address_component = 3; } // Actually load the operand. switch (operand.storage_source) { case InstructionStorageSource::kRegister: // *********************************************************************** // General-purpose register // *********************************************************************** if (IndexableGPRsUsed()) { // GPRs are in x0 - need to load to the intermediate register (indexable // temps are only accessible via mov load/store). if (dxbc_operand.intermediate_register == DxbcSourceOperand::kIntermediateRegisterNone) { dxbc_operand.intermediate_register = PushSystemTemp(); } dxbc_operand.type = DxbcSourceOperand::Type::kIntermediateRegister; if (operand.storage_addressing_mode == InstructionStorageAddressingMode::kStatic) { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(6)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(dxbc_operand.intermediate_register); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_INDEXABLE_TEMP, kSwizzleXYZW, 2)); shader_code_.push_back(0); shader_code_.push_back(uint32_t(operand.storage_index)); } else { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(8)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(dxbc_operand.intermediate_register); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_INDEXABLE_TEMP, kSwizzleXYZW, 2, D3D10_SB_OPERAND_INDEX_IMMEDIATE32, D3D10_SB_OPERAND_INDEX_IMMEDIATE32_PLUS_RELATIVE)); shader_code_.push_back(0); shader_code_.push_back(uint32_t(operand.storage_index)); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_TEMP, dynamic_address_component, 1)); shader_code_.push_back(dynamic_address_register); } ++stat_.instruction_count; ++stat_.array_instruction_count; } else { // GPRs are in r# - can access directly if addressed statically, load // by checking every register whether it's the needed one if addressed // dynamically. if (operand.storage_addressing_mode == InstructionStorageAddressingMode::kStatic) { dxbc_operand.type = DxbcSourceOperand::Type::kRegister; dxbc_operand.index = uint32_t(operand.storage_index); } else { if (dxbc_operand.intermediate_register == DxbcSourceOperand::kIntermediateRegisterNone) { dxbc_operand.intermediate_register = PushSystemTemp(); } dxbc_operand.type = DxbcSourceOperand::Type::kIntermediateRegister; uint32_t gpr_movc_mask_register = PushSystemTemp(); for (uint32_t i = 0; i < register_count(); ++i) { if ((i & 3) == 0) { // Compare the dynamic address to each register number to check if // it's the one that's needed. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IEQ) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(gpr_movc_mask_register); shader_code_.push_back(EncodeVectorReplicatedOperand( D3D10_SB_OPERAND_TYPE_TEMP, dynamic_address_component, 1)); shader_code_.push_back(dynamic_address_register); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); for (uint32_t j = 0; j < 4; ++j) { shader_code_.push_back(i + j - uint32_t(operand.storage_index)); } ++stat_.instruction_count; ++stat_.int_instruction_count; } shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(dxbc_operand.intermediate_register); shader_code_.push_back(EncodeVectorReplicatedOperand( D3D10_SB_OPERAND_TYPE_TEMP, i & 3, 1)); shader_code_.push_back(gpr_movc_mask_register); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(i); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(dxbc_operand.intermediate_register); ++stat_.instruction_count; ++stat_.movc_instruction_count; } // Release gpr_movc_mask_register. PopSystemTemp(); } } break; case InstructionStorageSource::kConstantFloat: // *********************************************************************** // Float constant // *********************************************************************** if (cbuffer_index_float_constants_ == kCbufferIndexUnallocated) { cbuffer_index_float_constants_ = cbuffer_count_++; } dxbc_operand.type = DxbcSourceOperand::Type::kConstantFloat; dxbc_operand.index = uint32_t(operand.storage_index); dxbc_operand.addressing_mode = operand.storage_addressing_mode; if (operand.storage_addressing_mode != InstructionStorageAddressingMode::kStatic) { float_constants_dynamic_indexed_ = true; } break; case InstructionStorageSource::kConstantInt: { // *********************************************************************** // Loop constant // *********************************************************************** if (cbuffer_index_bool_loop_constants_ == kCbufferIndexUnallocated) { cbuffer_index_bool_loop_constants_ = cbuffer_count_++; } // Convert to float and store in the intermediate register. // The constant buffer contains each integer replicated in XYZW so dynamic // indexing is possible. dxbc_operand.type = DxbcSourceOperand::Type::kIntermediateRegister; if (dxbc_operand.intermediate_register == DxbcSourceOperand::kIntermediateRegisterNone) { dxbc_operand.intermediate_register = PushSystemTemp(); } bool is_static = operand.storage_addressing_mode == InstructionStorageAddressingMode::kStatic; shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ITOF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(is_static ? 7 : 9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(dxbc_operand.intermediate_register); shader_code_.push_back(EncodeVectorReplicatedOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, 0, 3, D3D10_SB_OPERAND_INDEX_IMMEDIATE32, D3D10_SB_OPERAND_INDEX_IMMEDIATE32, is_static ? D3D10_SB_OPERAND_INDEX_IMMEDIATE32 : D3D10_SB_OPERAND_INDEX_IMMEDIATE32_PLUS_RELATIVE)); shader_code_.push_back(cbuffer_index_bool_loop_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kBoolLoopConstants)); // 8 to skip bool constants. shader_code_.push_back(8 + uint32_t(operand.storage_index)); if (!is_static) { shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_TEMP, dynamic_address_component, 1)); shader_code_.push_back(dynamic_address_register); bool_loop_constants_dynamic_indexed_ = true; } ++stat_.instruction_count; ++stat_.conversion_instruction_count; } break; case InstructionStorageSource::kConstantBool: { // *********************************************************************** // Boolean constant // *********************************************************************** if (cbuffer_index_bool_loop_constants_ == kCbufferIndexUnallocated) { cbuffer_index_bool_loop_constants_ = cbuffer_count_++; } // Extract, convert to float and store in the intermediate register. // The constant buffer contains each 32-bit vector replicated in XYZW so // dynamic indexing is possible. dxbc_operand.type = DxbcSourceOperand::Type::kIntermediateRegister; if (dxbc_operand.intermediate_register == DxbcSourceOperand::kIntermediateRegisterNone) { dxbc_operand.intermediate_register = PushSystemTemp(); } if (operand.storage_addressing_mode == InstructionStorageAddressingMode::kStatic) { // Extract the bit directly. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_UBFE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(11)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(dxbc_operand.intermediate_register); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(1); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(uint32_t(operand.storage_index) & 31); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, 0, 3)); shader_code_.push_back(cbuffer_index_bool_loop_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kBoolLoopConstants)); shader_code_.push_back(uint32_t(operand.storage_index) >> 5); ++stat_.instruction_count; ++stat_.uint_instruction_count; } else { bool_loop_constants_dynamic_indexed_ = true; uint32_t constant_address_register = dynamic_address_register; uint32_t constant_address_component = dynamic_address_component; if (operand.storage_index != 0) { // Has an offset - add it. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(dxbc_operand.intermediate_register); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_TEMP, constant_address_component, 1)); shader_code_.push_back(constant_address_register); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(uint32_t(operand.storage_index)); ++stat_.instruction_count; ++stat_.int_instruction_count; constant_address_register = dxbc_operand.intermediate_register; constant_address_component = 0; } // Split the index into constant index and bit offset and store them in // the intermediate register. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_UBFE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(15)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0011, 1)); shader_code_.push_back(dxbc_operand.intermediate_register); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(5); shader_code_.push_back(3); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(5); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(EncodeVectorReplicatedOperand( D3D10_SB_OPERAND_TYPE_TEMP, constant_address_component, 1)); shader_code_.push_back(constant_address_register); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Extract the bits. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_UBFE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(12)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(dxbc_operand.intermediate_register); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(1); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(dxbc_operand.intermediate_register); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, 0, 3, D3D10_SB_OPERAND_INDEX_IMMEDIATE32, D3D10_SB_OPERAND_INDEX_IMMEDIATE32, D3D10_SB_OPERAND_INDEX_RELATIVE)); shader_code_.push_back(cbuffer_index_bool_loop_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kBoolLoopConstants)); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(dxbc_operand.intermediate_register); ++stat_.instruction_count; ++stat_.uint_instruction_count; } // Convert the bit to float and replicate it. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_UTOF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(dxbc_operand.intermediate_register); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(dxbc_operand.intermediate_register); ++stat_.instruction_count; ++stat_.conversion_instruction_count; } break; default: // Fall back to constant zeros for invalid types. dxbc_operand.index = constant_component_values; dxbc_operand.swizzle = kSwizzleXYZW; return; } // If there are zeros or ones in the swizzle, force load the operand into the // intermediate register (applying the swizzle and the modifiers), and then // replace the components there. if (constant_components != 0) { if (dxbc_operand.intermediate_register == DxbcSourceOperand::kIntermediateRegisterNone) { dxbc_operand.intermediate_register = PushSystemTemp(); } shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 3 + DxbcSourceOperandLength(dxbc_operand))); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(dxbc_operand.intermediate_register); UseDxbcSourceOperand(dxbc_operand); ++stat_.instruction_count; ++stat_.mov_instruction_count; // Write the constant components. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(8)); shader_code_.push_back(EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, constant_components, 1)); shader_code_.push_back(dxbc_operand.intermediate_register); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); for (uint32_t i = 0; i < 4; ++i) { if (constant_component_values & (1 << i)) { shader_code_.push_back(operand.is_negated ? 0xBF800000u : 0x3F800000u); } else { shader_code_.push_back(0); } } ++stat_.instruction_count; ++stat_.mov_instruction_count; dxbc_operand.type = DxbcSourceOperand::Type::kIntermediateRegister; // Swizzle and modifiers already applied. dxbc_operand.swizzle = kSwizzleXYZW; dxbc_operand.is_negated = false; dxbc_operand.is_absolute_value = false; } } uint32_t DxbcShaderTranslator::DxbcSourceOperandLength( const DxbcSourceOperand& operand, bool negate, bool absolute) const { uint32_t length; switch (operand.type) { case DxbcSourceOperand::Type::kRegister: case DxbcSourceOperand::Type::kIntermediateRegister: // Either a game register (for non-indexable GPRs) or the intermediate // register with the data loaded (for indexable GPRs, bool and loop // constants). length = 2; break; case DxbcSourceOperand::Type::kConstantFloat: if (operand.addressing_mode != InstructionStorageAddressingMode::kStatic) { // Constant buffer, 3D index - immediate 0, immediate 1, immediate plus // register 2. length = 6; } else { // Constant buffer, 3D immediate index. length = 4; } break; default: // Pre-negated literal of zeros and ones (no extension dword), or a // totally invalid operand replaced by a literal. return 5; } // Apply both the operand negation and the usage negation (for subtraction) // and absolute from both sources. if (operand.is_negated) { negate = !negate; } absolute |= operand.is_absolute_value; // Modifier extension - neg/abs or non-uniform binding index. if (negate || absolute) { ++length; } return length; } void DxbcShaderTranslator::UseDxbcSourceOperand( const DxbcSourceOperand& operand, uint32_t additional_swizzle, uint32_t select_component, bool negate, bool absolute) { // Apply swizzle needed by the instruction implementation in addition to the // operand swizzle. uint32_t swizzle = 0; for (uint32_t i = 0; i < 4; ++i) { uint32_t swizzle_component = (additional_swizzle >> (i * 2)) & 3; swizzle |= ((operand.swizzle >> (swizzle_component * 2)) & 3) << (i * 2); } // Access either the whole vector or only one component of it, depending to // what is needed. uint32_t component_bits = ENCODE_D3D10_SB_OPERAND_NUM_COMPONENTS(D3D10_SB_OPERAND_4_COMPONENT); if (select_component <= 3) { component_bits |= ENCODE_D3D10_SB_OPERAND_4_COMPONENT_SELECTION_MODE( D3D10_SB_OPERAND_4_COMPONENT_SELECT_1_MODE) | (((swizzle >> (select_component * 2)) & 0x3) << D3D10_SB_OPERAND_4_COMPONENT_SELECT_1_SHIFT); } else { component_bits |= ENCODE_D3D10_SB_OPERAND_4_COMPONENT_SELECTION_MODE( D3D10_SB_OPERAND_4_COMPONENT_SWIZZLE_MODE) | (swizzle << D3D10_SB_OPERAND_4_COMPONENT_SWIZZLE_SHIFT); } // Apply both the operand negation and the usage negation (for subtraction) // and absolute value from both sources. if (operand.is_negated) { negate = !negate; } absolute |= operand.is_absolute_value; // Build OperandToken1 for modifiers (negate, absolute, minimum precision, // non-uniform binding index) - if it has any, it will be non-zero. // NOTE: AMD GPUs or drivers do NOT support non-uniform constant buffer // indices as of October 1, 2018 - they were causing significant skinned mesh // corruption when Xenia used multiple descriptors for float constants rather // than remapping. uint32_t modifiers = 0; if (negate && absolute) { modifiers |= D3D10_SB_OPERAND_MODIFIER_ABSNEG << D3D10_SB_OPERAND_MODIFIER_SHIFT; } else if (negate) { modifiers |= D3D10_SB_OPERAND_MODIFIER_NEG << D3D10_SB_OPERAND_MODIFIER_SHIFT; } else if (absolute) { modifiers |= D3D10_SB_OPERAND_MODIFIER_ABS << D3D10_SB_OPERAND_MODIFIER_SHIFT; } if (modifiers != 0) { // Mark the extension as containing modifiers. modifiers |= ENCODE_D3D10_SB_EXTENDED_OPERAND_TYPE( D3D10_SB_EXTENDED_OPERAND_MODIFIER); } uint32_t extended_bit = ENCODE_D3D10_SB_OPERAND_EXTENDED(modifiers); // Actually write the operand tokens. switch (operand.type) { case DxbcSourceOperand::Type::kRegister: shader_code_.push_back( ENCODE_D3D10_SB_OPERAND_TYPE(D3D10_SB_OPERAND_TYPE_TEMP) | ENCODE_D3D10_SB_OPERAND_INDEX_DIMENSION(D3D10_SB_OPERAND_INDEX_1D) | ENCODE_D3D10_SB_OPERAND_INDEX_REPRESENTATION( 0, D3D10_SB_OPERAND_INDEX_IMMEDIATE32) | component_bits | extended_bit); if (modifiers != 0) { shader_code_.push_back(modifiers); } shader_code_.push_back(operand.index); break; case DxbcSourceOperand::Type::kConstantFloat: { bool is_static = operand.addressing_mode == InstructionStorageAddressingMode::kStatic; shader_code_.push_back( ENCODE_D3D10_SB_OPERAND_TYPE(D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER) | ENCODE_D3D10_SB_OPERAND_INDEX_DIMENSION(D3D10_SB_OPERAND_INDEX_3D) | ENCODE_D3D10_SB_OPERAND_INDEX_REPRESENTATION( 0, D3D10_SB_OPERAND_INDEX_IMMEDIATE32) | ENCODE_D3D10_SB_OPERAND_INDEX_REPRESENTATION( 1, D3D10_SB_OPERAND_INDEX_IMMEDIATE32) | ENCODE_D3D10_SB_OPERAND_INDEX_REPRESENTATION( 2, is_static ? D3D10_SB_OPERAND_INDEX_IMMEDIATE32 : D3D10_SB_OPERAND_INDEX_IMMEDIATE32_PLUS_RELATIVE) | component_bits | extended_bit); if (modifiers != 0) { shader_code_.push_back(modifiers); } shader_code_.push_back(cbuffer_index_float_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kFloatConstants)); if (!float_constants_dynamic_indexed_) { // If there's no dynamic indexing in the shader, constants are compacted // and remapped. Store where the index has been written. float_constant_index_offsets_.push_back(uint32_t(shader_code_.size())); } shader_code_.push_back(operand.index); if (!is_static) { uint32_t dynamic_address_register, dynamic_address_component; if (operand.addressing_mode == InstructionStorageAddressingMode::kAddressRelative) { // Addressed by aL.x. dynamic_address_register = system_temp_aL_; dynamic_address_component = 0; } else { // Addressed by a0. dynamic_address_register = system_temp_ps_pc_p0_a0_; dynamic_address_component = 3; } shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_TEMP, dynamic_address_component, 1)); shader_code_.push_back(dynamic_address_register); } } break; case DxbcSourceOperand::Type::kIntermediateRegister: // Already loaded as float to the intermediate temporary register. shader_code_.push_back( ENCODE_D3D10_SB_OPERAND_TYPE(D3D10_SB_OPERAND_TYPE_TEMP) | ENCODE_D3D10_SB_OPERAND_INDEX_DIMENSION(D3D10_SB_OPERAND_INDEX_1D) | ENCODE_D3D10_SB_OPERAND_INDEX_REPRESENTATION( 0, D3D10_SB_OPERAND_INDEX_IMMEDIATE32) | component_bits | extended_bit); if (modifiers != 0) { shader_code_.push_back(modifiers); } shader_code_.push_back(operand.intermediate_register); break; default: // Only zeros and ones in the swizzle, or the safest replacement for an // invalid operand (such as a fetch constant). shader_code_.push_back( ENCODE_D3D10_SB_OPERAND_TYPE(D3D10_SB_OPERAND_TYPE_IMMEDIATE32) | ENCODE_D3D10_SB_OPERAND_INDEX_DIMENSION(D3D10_SB_OPERAND_INDEX_0D) | component_bits); for (uint32_t i = 0; i < 4; ++i) { if (operand.index & (1 << i)) { shader_code_.push_back(negate ? 0xBF800000u : 0x3F800000u); } else { shader_code_.push_back(0); } } } } void DxbcShaderTranslator::UnloadDxbcSourceOperand( const DxbcSourceOperand& operand) { if (operand.intermediate_register != DxbcSourceOperand::kIntermediateRegisterNone) { PopSystemTemp(); } } void DxbcShaderTranslator::StoreResult(const InstructionResult& result, uint32_t reg, bool replicate_x) { if (result.storage_target == InstructionStorageTarget::kNone || !result.has_any_writes()) { return; } uint32_t saturate_bit = ENCODE_D3D10_SB_INSTRUCTION_SATURATE(result.is_clamped); // Scalar targets get only one component. if (result.storage_target == InstructionStorageTarget::kPointSize || result.storage_target == InstructionStorageTarget::kDepth) { if (!result.write_mask[0]) { return; } SwizzleSource component = result.components[0]; if (replicate_x && component <= SwizzleSource::kW) { component = SwizzleSource::kX; } // Both r[imm32] and imm32 operands are 2 tokens long. switch (result.storage_target) { case InstructionStorageTarget::kPointSize: shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5) | saturate_bit); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_OUTPUT, 0b0100, 1)); shader_code_.push_back(uint32_t(InOutRegister::kVSOutPointParameters)); break; case InstructionStorageTarget::kDepth: writes_depth_ = true; if (edram_rov_used_) { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_INSTRUCTION_SATURATE(1) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_depth_); } else { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_INSTRUCTION_SATURATE(1) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(4)); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_OUTPUT_DEPTH, 0)); } break; default: assert_unhandled_case(result.storage_target); return; } if (component <= SwizzleSource::kW) { shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_TEMP, uint32_t(component), 1)); shader_code_.push_back(reg); } else { shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(component == SwizzleSource::k1 ? 0x3F800000 : 0); } ++stat_.instruction_count; ++stat_.mov_instruction_count; return; } // Get the write masks and data required for loading of both the swizzled part // and the constant (zero/one) part. The write mask is treated also as a read // mask in DXBC, and `mov r0.zw, r1.xyzw` actually means r0.zw = r1.zw, not // r0.zw = r1.xy. uint32_t swizzle_mask = 0; uint32_t swizzle_components = 0; uint32_t constant_mask = 0; uint32_t constant_values = 0; for (uint32_t i = 0; i < 4; ++i) { if (!result.write_mask[i]) { continue; } SwizzleSource component = result.components[i]; if (component <= SwizzleSource::kW) { swizzle_mask |= 1 << i; // If replicating X, just keep zero swizzle (XXXX). if (!replicate_x) { swizzle_components |= uint32_t(component) << (i * 2); } } else { constant_mask |= 1 << i; constant_values |= (component == SwizzleSource::k1 ? 1 : 0) << i; } } bool is_static = result.storage_addressing_mode == InstructionStorageAddressingMode::kStatic; // If the index is dynamic, choose where it's taken from. uint32_t dynamic_address_register, dynamic_address_component; if (result.storage_addressing_mode == InstructionStorageAddressingMode::kAddressRelative) { // Addressed by aL.x. dynamic_address_register = system_temp_aL_; dynamic_address_component = 0; } else { // Addressed by a0. dynamic_address_register = system_temp_ps_pc_p0_a0_; dynamic_address_component = 3; } // Temporary registers for storing dynamically indexed GPRs via movc. uint32_t gpr_movc_source_register = UINT32_MAX; uint32_t gpr_movc_mask_register = UINT32_MAX; if (result.storage_target == InstructionStorageTarget::kRegister && !is_static && !IndexableGPRsUsed()) { gpr_movc_source_register = PushSystemTemp(); gpr_movc_mask_register = PushSystemTemp(); } // Store both parts of the write (i == 0 - swizzled, i == 1 - constant). for (uint32_t i = 0; i < 2; ++i) { uint32_t mask = i == 0 ? swizzle_mask : constant_mask; if (mask == 0) { continue; } // r# for the swizzled part, 4-component imm32 for the constant part. uint32_t source_length = i != 0 ? 5 : 2; switch (result.storage_target) { case InstructionStorageTarget::kRegister: if (IndexableGPRsUsed()) { ++stat_.instruction_count; ++stat_.array_instruction_count; shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH((is_static ? 4 : 6) + source_length) | saturate_bit); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_INDEXABLE_TEMP, mask, 2, D3D10_SB_OPERAND_INDEX_IMMEDIATE32, is_static ? D3D10_SB_OPERAND_INDEX_IMMEDIATE32 : D3D10_SB_OPERAND_INDEX_IMMEDIATE32_PLUS_RELATIVE)); shader_code_.push_back(0); shader_code_.push_back(uint32_t(result.storage_index)); if (!is_static) { shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_TEMP, dynamic_address_component, 1)); shader_code_.push_back(dynamic_address_register); } } else { ++stat_.instruction_count; ++stat_.mov_instruction_count; shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3 + source_length) | saturate_bit); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, mask, 1)); shader_code_.push_back(is_static ? uint32_t(result.storage_index) : gpr_movc_source_register); } break; case InstructionStorageTarget::kInterpolant: ++stat_.instruction_count; ++stat_.mov_instruction_count; shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3 + source_length) | saturate_bit); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_OUTPUT, mask, 1)); shader_code_.push_back(uint32_t(InOutRegister::kVSOutInterpolators) + uint32_t(result.storage_index)); break; case InstructionStorageTarget::kPosition: ++stat_.instruction_count; ++stat_.mov_instruction_count; shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3 + source_length) | saturate_bit); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, mask, 1)); shader_code_.push_back(system_temp_position_); break; case InstructionStorageTarget::kColorTarget: ++stat_.instruction_count; ++stat_.mov_instruction_count; shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3 + source_length) | saturate_bit); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, mask, 1)); shader_code_.push_back( system_temp_color_[uint32_t(result.storage_index)]); break; default: continue; } if (i == 0) { // Copy from the source r#. shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, swizzle_components, 1)); shader_code_.push_back(reg); } else { // Load constants. shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); for (uint32_t j = 0; j < 4; ++j) { shader_code_.push_back((constant_values & (1 << j)) ? 0x3F800000 : 0); } } } // Store to the GPR using lots of movc instructions if not using indexable // temps, but the target has a relative address. if (gpr_movc_source_register != UINT32_MAX) { for (uint32_t i = 0; i < register_count(); ++i) { if ((i & 3) == 0) { // Compare the dynamic address to each register number to check if it's // the one that's needed. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IEQ) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(gpr_movc_mask_register); shader_code_.push_back(EncodeVectorReplicatedOperand( D3D10_SB_OPERAND_TYPE_TEMP, dynamic_address_component, 1)); shader_code_.push_back(dynamic_address_register); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); for (uint32_t j = 0; j < 4; ++j) { shader_code_.push_back(i + j - uint32_t(result.storage_index)); } ++stat_.instruction_count; ++stat_.int_instruction_count; } shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(i); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, i & 3, 1)); shader_code_.push_back(gpr_movc_mask_register); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(gpr_movc_source_register); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(i); ++stat_.instruction_count; ++stat_.movc_instruction_count; } PopSystemTemp(2); } if (edram_rov_used_ && result.storage_target == InstructionStorageTarget::kColorTarget) { // For ROV output, mark that the color has been written to. // According to: // https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/dx9-graphics-reference-asm-ps-registers-output-color // if a color target has been written to - including due to flow control - // the render target must not be modified (the unwritten components of a // written target are undefined, but let's keep the original value in this // case). shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_OR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, 1 << uint32_t(result.storage_index), 1)); shader_code_.push_back(system_temp_color_written_); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_TEMP, uint32_t(result.storage_index), 1)); shader_code_.push_back(system_temp_color_written_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(swizzle_mask | constant_mask); ++stat_.instruction_count; ++stat_.uint_instruction_count; } } void DxbcShaderTranslator::UpdateExecConditionals( ParsedExecInstruction::Type type, uint32_t bool_constant_index, bool condition, bool emit_disassembly) { // Check if we can merge the new exec with the previous one, or the jump with // the previous exec. The instruction-level predicate check is also merged in // this case. bool merge = false; if (type == ParsedExecInstruction::Type::kConditional) { // Can merge conditional with conditional, as long as the bool constant and // the expected values are the same. if (cf_exec_bool_constant_ == bool_constant_index && cf_exec_bool_constant_condition_ == condition) { merge = true; } } else if (type == ParsedExecInstruction::Type::kPredicated) { // Can merge predicated with predicated if the conditions are the same and // the previous exec hasn't modified the predicate register. if (!cf_exec_predicate_written_ && cf_exec_predicated_ && cf_exec_predicate_condition_ == condition) { merge = true; } } else { // Can merge unconditional with unconditional. if (cf_exec_bool_constant_ == kCfExecBoolConstantNone && !cf_exec_predicated_) { merge = true; } } if (merge) { // Emit the disassembly for the exec/jump merged with the previous one. if (emit_disassembly) { EmitInstructionDisassembly(); } return; } CloseExecConditionals(); // Emit the disassembly for the new exec/jump. if (emit_disassembly) { EmitInstructionDisassembly(); } D3D10_SB_INSTRUCTION_TEST_BOOLEAN test = condition ? D3D10_SB_INSTRUCTION_TEST_NONZERO : D3D10_SB_INSTRUCTION_TEST_ZERO; if (type == ParsedExecInstruction::Type::kConditional) { uint32_t bool_constant_test_register = PushSystemTemp(); // Check the bool constant value. if (cbuffer_index_bool_loop_constants_ == kCbufferIndexUnallocated) { cbuffer_index_bool_loop_constants_ = cbuffer_count_++; } shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(bool_constant_test_register); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, 0, 3)); shader_code_.push_back(cbuffer_index_bool_loop_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kBoolLoopConstants)); shader_code_.push_back(bool_constant_index >> 5); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(1u << (bool_constant_index & 31)); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Open the new `if`. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IF) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN(test) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(bool_constant_test_register); ++stat_.instruction_count; ++stat_.dynamic_flow_control_count; // Release bool_constant_test_register. PopSystemTemp(); cf_exec_bool_constant_ = bool_constant_index; cf_exec_bool_constant_condition_ = condition; } else if (type == ParsedExecInstruction::Type::kPredicated) { shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IF) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN(test) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); ++stat_.instruction_count; ++stat_.dynamic_flow_control_count; cf_exec_predicated_ = true; cf_exec_predicate_condition_ = condition; } } void DxbcShaderTranslator::CloseExecConditionals() { // Within the exec - instruction-level predicate check. CloseInstructionPredication(); // Exec level. if (cf_exec_bool_constant_ != kCfExecBoolConstantNone || cf_exec_predicated_) { shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; cf_exec_bool_constant_ = kCfExecBoolConstantNone; cf_exec_predicated_ = false; } // Nothing relies on the predicate value being unchanged now. cf_exec_predicate_written_ = false; } void DxbcShaderTranslator::UpdateInstructionPredication(bool predicated, bool condition, bool emit_disassembly) { if (predicated) { if (cf_instruction_predicate_if_open_) { if (cf_instruction_predicate_condition_ == condition) { // Already in the needed instruction-level `if`. if (emit_disassembly) { EmitInstructionDisassembly(); } return; } CloseInstructionPredication(); } // Emit the disassembly before opening (or not opening) the new conditional. if (emit_disassembly) { EmitInstructionDisassembly(); } // If the instruction predicate condition is the same as the exec predicate // condition, no need to open a check. However, if there was a `setp` prior // to this instruction, the predicate value now may be different than it was // in the beginning of the exec. if (!cf_exec_predicate_written_ && cf_exec_predicated_ && cf_exec_predicate_condition_ == condition) { return; } D3D10_SB_INSTRUCTION_TEST_BOOLEAN test = condition ? D3D10_SB_INSTRUCTION_TEST_NONZERO : D3D10_SB_INSTRUCTION_TEST_ZERO; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IF) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN(test) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); ++stat_.instruction_count; ++stat_.dynamic_flow_control_count; cf_instruction_predicate_if_open_ = true; cf_instruction_predicate_condition_ = condition; } else { CloseInstructionPredication(); if (emit_disassembly) { EmitInstructionDisassembly(); } } } void DxbcShaderTranslator::CloseInstructionPredication() { if (cf_instruction_predicate_if_open_) { shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; cf_instruction_predicate_if_open_ = false; } } void DxbcShaderTranslator::JumpToLabel(uint32_t address) { shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(address); ++stat_.instruction_count; ++stat_.mov_instruction_count; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_CONTINUE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; } void DxbcShaderTranslator::SwapVertexData(uint32_t vfetch_index, uint32_t write_mask) { // Make sure we have fetch constants. if (cbuffer_index_fetch_constants_ == kCbufferIndexUnallocated) { cbuffer_index_fetch_constants_ = cbuffer_count_++; } // Allocate temporary registers for intermediate values. uint32_t temp1 = PushSystemTemp(); uint32_t temp2 = PushSystemTemp(); // 8-in-16: Create the value being built in temp1. // ushr temp1, pv, l(8, 8, 8, 8) // pv: ABCD, temp1: BCD0 shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_USHR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, write_mask, 1)); shader_code_.push_back(temp1); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(8); shader_code_.push_back(8); shader_code_.push_back(8); shader_code_.push_back(8); ++stat_.instruction_count; ++stat_.uint_instruction_count; // 8-in-16: Insert A in Y of temp1. // bfi temp1, l(8, 8, 8, 8), l(8, 8, 8, 8), pv, temp1 // pv: ABCD, temp1: BAD0 shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_BFI) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(17)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, write_mask, 1)); shader_code_.push_back(temp1); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(8); shader_code_.push_back(8); shader_code_.push_back(8); shader_code_.push_back(8); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(8); shader_code_.push_back(8); shader_code_.push_back(8); shader_code_.push_back(8); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(temp1); ++stat_.instruction_count; ++stat_.uint_instruction_count; // 8-in-16: Create the source for C insertion in temp2. // ushr temp2, pv, l(16, 16, 16, 16) // pv: ABCD, temp1: BAD0, temp2: CD00 shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_USHR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, write_mask, 1)); shader_code_.push_back(temp2); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(16); shader_code_.push_back(16); shader_code_.push_back(16); shader_code_.push_back(16); ++stat_.instruction_count; ++stat_.uint_instruction_count; // 8-in-16: Insert C in W of temp1. // bfi temp1, l(8, 8, 8, 8), l(24, 24, 24, 24), temp2, temp1 // pv: ABCD, temp1: BADC shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_BFI) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(17)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, write_mask, 1)); shader_code_.push_back(temp1); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(8); shader_code_.push_back(8); shader_code_.push_back(8); shader_code_.push_back(8); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(24); shader_code_.push_back(24); shader_code_.push_back(24); shader_code_.push_back(24); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(temp2); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(temp1); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Get bits indicating what swaps should be done. The endianness is located in // the low 2 bits of the second dword of the fetch constant: // - 00 for no swap. // - 01 for 8-in-16. // - 10 for 8-in-32 (8-in-16 and 16-in-32). // - 11 for 16-in-32. // ubfe temp2.xy, l(1, 1), l(0, 1), fetch.yy // pv: ABCD, temp1: BADC, temp2: 8in16/16in32?|8in32/16in32? shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_UBFE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(17)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0011, 1)); shader_code_.push_back(temp2); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(1); shader_code_.push_back(1); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(1); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(EncodeVectorReplicatedOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, (vfetch_index & 1) * 2 + 1, 3)); shader_code_.push_back(cbuffer_index_fetch_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kFetchConstants)); shader_code_.push_back(vfetch_index >> 1); ++stat_.instruction_count; ++stat_.uint_instruction_count; // 16-in-32 is used as intermediate swapping step here rather than 8-in-32. // Thus 8-in-16 needs to be done for 8-in-16 (01) and 8-in-32 (10). // And 16-in-32 needs to be done for 8-in-32 (10) and 16-in-32 (11). // xor temp2.x, temp2.x, temp2.y // pv: ABCD, temp1: BADC, temp2: 8in16/8in32?|8in32/16in32? shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_XOR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(temp2); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(temp2); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(temp2); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Write the 8-in-16 value to pv if needed. // movc pv, temp2.xxxx, temp1, pv // pv: ABCD/BADC, temp2: 8in16/8in32?|8in32/16in32? shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, write_mask, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(temp2); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(temp1); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); ++stat_.instruction_count; ++stat_.movc_instruction_count; // 16-in-32: Write the low 16 bits to temp1. // ushr temp1, pv, l(16, 16, 16, 16) // pv: ABCD/BADC, temp1: CD00/DC00, temp2: 8in16/8in32?|8in32/16in32? shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_USHR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, write_mask, 1)); shader_code_.push_back(temp1); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(16); shader_code_.push_back(16); shader_code_.push_back(16); shader_code_.push_back(16); ++stat_.instruction_count; ++stat_.uint_instruction_count; // 16-in-32: Write the high 16 bits to temp1. // bfi temp1, l(16, 16, 16, 16), l(16, 16, 16, 16), pv, temp1 // pv: ABCD/BADC, temp1: CDAB/DCBA, temp2: 8in16/8in32?|8in32/16in32? shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_BFI) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(17)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, write_mask, 1)); shader_code_.push_back(temp1); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(16); shader_code_.push_back(16); shader_code_.push_back(16); shader_code_.push_back(16); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(16); shader_code_.push_back(16); shader_code_.push_back(16); shader_code_.push_back(16); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(temp1); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Write the swapped value to pv. // movc pv, temp2.yyyy, temp1, pv shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, write_mask, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(temp2); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(temp1); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); ++stat_.instruction_count; ++stat_.movc_instruction_count; PopSystemTemp(2); } void DxbcShaderTranslator::ProcessLabel(uint32_t cf_index) { if (cf_index == 0) { // 0 already added in the beginning. return; } // Close flow control on the deeper levels below - prevent attempts to merge // execs across labels. CloseExecConditionals(); if (FLAGS_dxbc_switch) { // Fallthrough to the label from the previous one on the next iteration if // no `continue` was done. Can't simply fallthrough because in DXBC, a // non-empty switch case must end with a break. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(cf_index); ++stat_.instruction_count; ++stat_.mov_instruction_count; shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_CONTINUE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; // Close the previous label. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_BREAK) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; // Go to the next label. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_CASE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(cf_index); ++stat_.instruction_count; ++stat_.static_flow_control_count; } else { // Close the previous label. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; // pc <= cf_index uint32_t test_register = PushSystemTemp(); shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_UGE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(test_register); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(cf_index); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); ++stat_.instruction_count; ++stat_.uint_instruction_count; // if (pc <= cf_index) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN( D3D10_SB_INSTRUCTION_TEST_NONZERO)); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(test_register); ++stat_.instruction_count; ++stat_.dynamic_flow_control_count; PopSystemTemp(); } } void DxbcShaderTranslator::ProcessExecInstructionBegin( const ParsedExecInstruction& instr) { if (FLAGS_dxbc_source_map) { instruction_disassembly_buffer_.Reset(); instr.Disassemble(&instruction_disassembly_buffer_); // Will be emitted by UpdateExecConditionals. } UpdateExecConditionals(instr.type, instr.bool_constant_index, instr.condition, true); // TODO(Triang3l): Find out what PredicateClean=false in exec actually means // (execs containing setp have PredicateClean=false, it possibly means that // the predicate is dirty after the exec). } void DxbcShaderTranslator::ProcessExecInstructionEnd( const ParsedExecInstruction& instr) { // TODO(Triang3l): Check whether is_end is conditional or not. if (instr.is_end) { // Break out of the main loop. CloseInstructionPredication(); if (FLAGS_dxbc_switch) { // Write an invalid value to pc. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0xFFFFFFFFu); ++stat_.instruction_count; ++stat_.mov_instruction_count; // Go to the next iteration, where switch cases won't be reached. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_CONTINUE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; } else { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_BREAK) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; } } } void DxbcShaderTranslator::ProcessLoopStartInstruction( const ParsedLoopStartInstruction& instr) { // loop il, L - loop with loop data il, end @ L // Loop control is outside execs - actually close the last exec. CloseExecConditionals(); if (FLAGS_dxbc_source_map) { instruction_disassembly_buffer_.Reset(); instr.Disassemble(&instruction_disassembly_buffer_); EmitInstructionDisassembly(); } uint32_t loop_count_and_aL = PushSystemTemp(); // Count (as uint) in bits 0:7 of the loop constant, aL in 8:15. if (cbuffer_index_bool_loop_constants_ == kCbufferIndexUnallocated) { cbuffer_index_bool_loop_constants_ = cbuffer_count_++; } shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_UBFE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(17)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0011, 1)); shader_code_.push_back(loop_count_and_aL); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(8); shader_code_.push_back(8); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(8); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(EncodeVectorReplicatedOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, 0, 3)); shader_code_.push_back(cbuffer_index_bool_loop_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kBoolLoopConstants)); // 8 because of bool constants. shader_code_.push_back(8 + instr.loop_constant_index); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Push the count to the loop count stack - move XYZ to YZW and set X to this // loop count. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1110, 1)); shader_code_.push_back(system_temp_loop_count_); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b10010000, 1)); shader_code_.push_back(system_temp_loop_count_); ++stat_.instruction_count; ++stat_.mov_instruction_count; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_loop_count_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(loop_count_and_aL); ++stat_.instruction_count; ++stat_.mov_instruction_count; // Push aL - keep the same value as in the previous loop if repeating, or the // new one otherwise. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(system_temp_aL_); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b10010000, 1)); shader_code_.push_back(system_temp_aL_); ++stat_.instruction_count; ++stat_.mov_instruction_count; if (!instr.is_repeat) { shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_aL_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(loop_count_and_aL); ++stat_.instruction_count; ++stat_.mov_instruction_count; } // Release loop_count_and_aL. PopSystemTemp(); // Short-circuit if loop counter is 0. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IF) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN(D3D10_SB_INSTRUCTION_TEST_ZERO) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_loop_count_); ++stat_.instruction_count; ++stat_.dynamic_flow_control_count; JumpToLabel(instr.loop_skip_address); shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; } void DxbcShaderTranslator::ProcessLoopEndInstruction( const ParsedLoopEndInstruction& instr) { // endloop il, L - end loop w/ data il, head @ L // Loop control is outside execs - actually close the last exec. CloseExecConditionals(); if (FLAGS_dxbc_source_map) { instruction_disassembly_buffer_.Reset(); instr.Disassemble(&instruction_disassembly_buffer_); EmitInstructionDisassembly(); } // Subtract 1 from the loop counter. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_loop_count_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_loop_count_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(uint32_t(-1)); ++stat_.instruction_count; ++stat_.int_instruction_count; // Break case. if (instr.is_predicated_break) { // if (loop_count.x == 0 || [!]p0) uint32_t break_case_temp = PushSystemTemp(); shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(break_case_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); if (instr.predicate_condition) { // If p0 is non-zero, set the test value to 0 (since if_z is used, // otherwise check if the loop counter is zero). shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0); } shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_loop_count_); if (!instr.predicate_condition) { // If p0 is zero, set the test value to 0 (since if_z is used, otherwise // check if the loop counter is zero). shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0); } ++stat_.instruction_count; ++stat_.movc_instruction_count; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IF) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN( D3D10_SB_INSTRUCTION_TEST_ZERO) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(break_case_temp); PopSystemTemp(); } else { // if (loop_count.x == 0) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IF) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN( D3D10_SB_INSTRUCTION_TEST_ZERO) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_loop_count_); } ++stat_.instruction_count; ++stat_.dynamic_flow_control_count; // Pop the current loop off the stack, move YZW to XYZ and set W to 0. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(system_temp_loop_count_); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b11111001, 1)); shader_code_.push_back(system_temp_loop_count_); ++stat_.instruction_count; ++stat_.mov_instruction_count; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1000, 1)); shader_code_.push_back(system_temp_loop_count_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.mov_instruction_count; // Now going to fall through to the next exec (no need to jump). // Continue case. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ELSE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; uint32_t aL_add_temp = PushSystemTemp(); // Extract the value to add to aL (in bits 16:23 of the loop constant). if (cbuffer_index_bool_loop_constants_ == kCbufferIndexUnallocated) { cbuffer_index_bool_loop_constants_ = cbuffer_count_++; } shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_UBFE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(11)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(aL_add_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(8); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(16); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, 0, 3)); shader_code_.push_back(cbuffer_index_bool_loop_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kBoolLoopConstants)); // 8 because of bool constants. shader_code_.push_back(8 + instr.loop_constant_index); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Add the needed value to aL. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_aL_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_loop_count_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(aL_add_temp); ++stat_.instruction_count; ++stat_.int_instruction_count; // Release aL_add_temp. PopSystemTemp(); // Jump back to the beginning of the loop body. JumpToLabel(instr.loop_body_address); shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; } void DxbcShaderTranslator::ProcessJumpInstruction( const ParsedJumpInstruction& instr) { if (FLAGS_dxbc_source_map) { instruction_disassembly_buffer_.Reset(); instr.Disassemble(&instruction_disassembly_buffer_); // Will be emitted by UpdateExecConditionals. } // Treat like exec, merge with execs if possible, since it's an if too. ParsedExecInstruction::Type type; if (instr.type == ParsedJumpInstruction::Type::kConditional) { type = ParsedExecInstruction::Type::kConditional; } else if (instr.type == ParsedJumpInstruction::Type::kPredicated) { type = ParsedExecInstruction::Type::kPredicated; } else { type = ParsedExecInstruction::Type::kUnconditional; } UpdateExecConditionals(type, instr.bool_constant_index, instr.condition, true); // UpdateExecConditionals may not necessarily close the instruction-level // predicate check (it's not necessary if the execs are merged), but here the // instruction itself is on the flow control level, so the predicate check is // on the flow control level too. CloseInstructionPredication(); JumpToLabel(instr.target_address); } void DxbcShaderTranslator::ProcessVertexFetchInstruction( const ParsedVertexFetchInstruction& instr) { if (instr.operand_count < 2 || instr.operands[1].storage_source != InstructionStorageSource::kVertexFetchConstant) { assert_always(); return; } // Get the mask for ld_raw and byte swapping. uint32_t load_dword_count; switch (instr.attributes.data_format) { case VertexFormat::k_8_8_8_8: case VertexFormat::k_2_10_10_10: case VertexFormat::k_10_11_11: case VertexFormat::k_11_11_10: case VertexFormat::k_16_16: case VertexFormat::k_16_16_FLOAT: case VertexFormat::k_32: case VertexFormat::k_32_FLOAT: load_dword_count = 1; break; case VertexFormat::k_16_16_16_16: case VertexFormat::k_16_16_16_16_FLOAT: case VertexFormat::k_32_32: case VertexFormat::k_32_32_FLOAT: load_dword_count = 2; break; case VertexFormat::k_32_32_32_FLOAT: load_dword_count = 3; break; case VertexFormat::k_32_32_32_32: case VertexFormat::k_32_32_32_32_FLOAT: load_dword_count = 4; break; default: assert_unhandled_case(instr.attributes.data_format); return; } // Get the result write mask. uint32_t result_component_count = GetVertexFormatComponentCount(instr.attributes.data_format); if (result_component_count == 0) { assert_always(); return; } uint32_t result_write_mask = (1 << result_component_count) - 1; if (FLAGS_dxbc_source_map) { instruction_disassembly_buffer_.Reset(); instr.Disassemble(&instruction_disassembly_buffer_); // Will be emitted by UpdateInstructionPredication. } UpdateInstructionPredication(instr.is_predicated, instr.predicate_condition, true); // Convert the index to an integer. DxbcSourceOperand index_operand; LoadDxbcSourceOperand(instr.operands[0], index_operand); shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_FTOI) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 3 + DxbcSourceOperandLength(index_operand))); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_pv_); UseDxbcSourceOperand(index_operand, kSwizzleXYZW, 0); ++stat_.instruction_count; ++stat_.conversion_instruction_count; UnloadDxbcSourceOperand(index_operand); // TODO(Triang3l): Index clamping maybe. uint32_t vfetch_index = instr.operands[1].storage_index; // Get the memory address (taken from the fetch constant - the low 2 bits of // it are removed because vertices and raw buffer operations are 4-aligned and // fetch type - 3 for vertices - is stored there). Vertex fetch is specified // by 2 dwords in fetch constants, but in our case they are 4-component, so // one vector of fetch constants contains two vfetches. // TODO(Triang3l): Clamp to buffer size maybe (may be difficult if the buffer // is smaller than 16). // http://xboxforums.create.msdn.com/forums/p/7537/39919.aspx#39919 if (cbuffer_index_fetch_constants_ == kCbufferIndexUnallocated) { cbuffer_index_fetch_constants_ = cbuffer_count_++; } shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, (vfetch_index & 1) * 2, 3)); shader_code_.push_back(cbuffer_index_fetch_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kFetchConstants)); shader_code_.push_back(vfetch_index >> 1); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0x1FFFFFFC); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Calculate the address of the vertex. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IMAD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(instr.attributes.stride * 4); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(system_temp_pv_); ++stat_.instruction_count; ++stat_.int_instruction_count; // Add the element offset. if (instr.attributes.offset != 0) { shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(instr.attributes.offset * 4); ++stat_.instruction_count; ++stat_.int_instruction_count; } // Load the vertex data from the shared memory at T0, register t0. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_LD_RAW) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(8)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, (1 << load_dword_count) - 1, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_RESOURCE, kSwizzleXYZW & ((1 << (load_dword_count * 2)) - 1), 2)); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.texture_load_instructions; // Byte swap the data. SwapVertexData(vfetch_index, (1 << load_dword_count) - 1); // Get the data needed for unpacking and converting. bool extract_signed = instr.attributes.is_signed; uint32_t extract_widths[4] = {}, extract_offsets[4] = {}; uint32_t extract_swizzle = kSwizzleXXXX; float normalize_scales[4] = {}; switch (instr.attributes.data_format) { case VertexFormat::k_8_8_8_8: extract_widths[0] = extract_widths[1] = extract_widths[2] = extract_widths[3] = 8; // Assuming little endian ByteAddressBuffer Load. extract_offsets[1] = 8; extract_offsets[2] = 16; extract_offsets[3] = 24; normalize_scales[0] = normalize_scales[1] = normalize_scales[2] = normalize_scales[3] = instr.attributes.is_signed ? (1.0f / 127.0f) : (1.0f / 255.0f); break; case VertexFormat::k_2_10_10_10: extract_widths[0] = extract_widths[1] = extract_widths[2] = 10; extract_widths[3] = 2; extract_offsets[1] = 10; extract_offsets[2] = 20; extract_offsets[3] = 30; normalize_scales[0] = normalize_scales[1] = normalize_scales[2] = instr.attributes.is_signed ? (1.0f / 511.0f) : (1.0f / 1023.0f); normalize_scales[3] = instr.attributes.is_signed ? 1.0f : (1.0f / 3.0f); break; case VertexFormat::k_10_11_11: extract_widths[0] = extract_widths[1] = 11; extract_widths[2] = 10; extract_offsets[1] = 11; extract_offsets[2] = 22; normalize_scales[0] = normalize_scales[1] = instr.attributes.is_signed ? (1.0f / 1023.0f) : (1.0f / 2047.0f); normalize_scales[2] = instr.attributes.is_signed ? (1.0f / 511.0f) : (1.0f / 1023.0f); break; case VertexFormat::k_11_11_10: extract_widths[0] = 10; extract_widths[1] = extract_widths[2] = 11; extract_offsets[1] = 10; extract_offsets[2] = 21; normalize_scales[0] = instr.attributes.is_signed ? (1.0f / 511.0f) : (1.0f / 1023.0f); normalize_scales[1] = normalize_scales[2] = instr.attributes.is_signed ? (1.0f / 1023.0f) : (1.0f / 2047.0f); break; case VertexFormat::k_16_16: extract_widths[0] = extract_widths[1] = 16; extract_offsets[1] = 16; normalize_scales[0] = normalize_scales[1] = instr.attributes.is_signed ? (1.0f / 32767.0f) : (1.0f / 65535.0f); break; case VertexFormat::k_16_16_16_16: extract_widths[0] = extract_widths[1] = extract_widths[2] = extract_widths[3] = 16; extract_offsets[1] = extract_offsets[3] = 16; extract_swizzle = 0b01010000; normalize_scales[0] = normalize_scales[1] = normalize_scales[2] = normalize_scales[3] = instr.attributes.is_signed ? (1.0f / 32767.0f) : (1.0f / 65535.0f); break; case VertexFormat::k_16_16_FLOAT: extract_signed = false; extract_widths[0] = extract_widths[1] = 16; extract_offsets[1] = 16; break; case VertexFormat::k_16_16_16_16_FLOAT: extract_signed = false; extract_widths[0] = extract_widths[1] = extract_widths[2] = extract_widths[3] = 16; extract_offsets[1] = extract_offsets[3] = 16; extract_swizzle = 0b01010000; break; // For 32-bit, extraction is not done at all, so its parameters are ignored. case VertexFormat::k_32: case VertexFormat::k_32_32: case VertexFormat::k_32_32_32_32: normalize_scales[0] = normalize_scales[1] = normalize_scales[2] = normalize_scales[3] = instr.attributes.is_signed ? (1.0f / 2147483647.0f) : (1.0f / 4294967295.0f); break; default: // 32-bit float. break; } // Extract components from packed data if needed. if (extract_widths[0] != 0) { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(extract_signed ? D3D11_SB_OPCODE_IBFE : D3D11_SB_OPCODE_UBFE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(15)); shader_code_.push_back(EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, result_write_mask, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(extract_widths[0]); shader_code_.push_back(extract_widths[1]); shader_code_.push_back(extract_widths[2]); shader_code_.push_back(extract_widths[3]); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(extract_offsets[0]); shader_code_.push_back(extract_offsets[1]); shader_code_.push_back(extract_offsets[2]); shader_code_.push_back(extract_offsets[3]); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, extract_swizzle, 1)); shader_code_.push_back(system_temp_pv_); ++stat_.instruction_count; if (extract_signed) { ++stat_.int_instruction_count; } else { ++stat_.uint_instruction_count; } } // Convert to float and normalize if needed. if (instr.attributes.data_format == VertexFormat::k_16_16_FLOAT || instr.attributes.data_format == VertexFormat::k_16_16_16_16_FLOAT) { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_F16TOF32) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back(EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, result_write_mask, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); ++stat_.instruction_count; ++stat_.conversion_instruction_count; } else if (normalize_scales[0] != 0.0f) { // If no normalize_scales, it's a float value already. Otherwise, convert to // float and normalize if needed. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(instr.attributes.is_signed ? D3D10_SB_OPCODE_ITOF : D3D10_SB_OPCODE_UTOF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back(EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, result_write_mask, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); ++stat_.instruction_count; ++stat_.conversion_instruction_count; if (!instr.attributes.is_integer) { // Normalize. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MUL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, result_write_mask, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); for (uint32_t i = 0; i < 4; ++i) { shader_code_.push_back( reinterpret_cast(normalize_scales)[i]); } ++stat_.instruction_count; ++stat_.float_instruction_count; // Clamp to -1 (both -127 and -128 should be -1 in graphics APIs for // snorm8). if (instr.attributes.is_signed) { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAX) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, result_write_mask, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0xBF800000u); shader_code_.push_back(0xBF800000u); shader_code_.push_back(0xBF800000u); shader_code_.push_back(0xBF800000u); ++stat_.instruction_count; ++stat_.float_instruction_count; } } } // Zero unused components if loaded a 32-bit component (because it's not // bfe'd, in this case, the unused components would have been zeroed already). if (extract_widths[0] == 0 && result_write_mask != 0b1111) { shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(8)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, 0b1111 & ~result_write_mask, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.mov_instruction_count; } // Apply the exponent bias. if (instr.attributes.exp_adjust != 0) { shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MUL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back(EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, result_write_mask, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); uint32_t exp_adjust_scale = uint32_t(0x3F800000 + (instr.attributes.exp_adjust << 23)); shader_code_.push_back(exp_adjust_scale); shader_code_.push_back(exp_adjust_scale); shader_code_.push_back(exp_adjust_scale); shader_code_.push_back(exp_adjust_scale); ++stat_.instruction_count; ++stat_.float_instruction_count; } StoreResult(instr.result, system_temp_pv_, false); } uint32_t DxbcShaderTranslator::FindOrAddTextureSRV(uint32_t fetch_constant, TextureDimension dimension, bool is_signed, bool is_sign_required) { // 1D and 2D textures (including stacked ones) are treated as 2D arrays for // binding and coordinate simplicity. if (dimension == TextureDimension::k1D) { dimension = TextureDimension::k2D; } // 1 is added to the return value because T0/t0 is shared memory. for (uint32_t i = 0; i < uint32_t(texture_srvs_.size()); ++i) { TextureSRV& texture_srv = texture_srvs_[i]; if (texture_srv.fetch_constant == fetch_constant && texture_srv.dimension == dimension && texture_srv.is_signed == is_signed) { if (is_sign_required && !texture_srv.is_sign_required) { // kGetTextureComputedLod uses only the unsigned SRV, which means it // must be bound even when all components are signed. texture_srv.is_sign_required = true; } return 1 + i; } } if (texture_srvs_.size() >= kMaxTextureSRVs) { assert_always(); return 1 + (kMaxTextureSRVs - 1); } TextureSRV new_texture_srv; new_texture_srv.fetch_constant = fetch_constant; new_texture_srv.dimension = dimension; new_texture_srv.is_signed = is_signed; new_texture_srv.is_sign_required = is_sign_required; const char* dimension_name; switch (dimension) { case TextureDimension::k3D: dimension_name = "3d"; break; case TextureDimension::kCube: dimension_name = "cube"; break; default: dimension_name = "2d"; } new_texture_srv.name = xe::format_string("xe_texture%u_%s_%c", fetch_constant, dimension_name, is_signed ? 's' : 'u'); uint32_t srv_register = 1 + uint32_t(texture_srvs_.size()); texture_srvs_.emplace_back(std::move(new_texture_srv)); return srv_register; } uint32_t DxbcShaderTranslator::FindOrAddSamplerBinding( uint32_t fetch_constant, TextureFilter mag_filter, TextureFilter min_filter, TextureFilter mip_filter, AnisoFilter aniso_filter) { // In Direct3D 12, anisotropic filtering implies linear filtering. if (aniso_filter != AnisoFilter::kDisabled && aniso_filter != AnisoFilter::kUseFetchConst) { mag_filter = TextureFilter::kLinear; min_filter = TextureFilter::kLinear; mip_filter = TextureFilter::kLinear; aniso_filter = std::min(aniso_filter, AnisoFilter::kMax_16_1); } for (uint32_t i = 0; i < uint32_t(sampler_bindings_.size()); ++i) { const SamplerBinding& sampler_binding = sampler_bindings_[i]; if (sampler_binding.fetch_constant == fetch_constant && sampler_binding.mag_filter == mag_filter && sampler_binding.min_filter == min_filter && sampler_binding.mip_filter == mip_filter && sampler_binding.aniso_filter == aniso_filter) { return i; } } if (sampler_bindings_.size() >= kMaxSamplerBindings) { assert_always(); return kMaxSamplerBindings - 1; } std::ostringstream name; name << "xe_sampler" << fetch_constant; if (aniso_filter != AnisoFilter::kUseFetchConst) { if (aniso_filter == AnisoFilter::kDisabled) { name << "_a0"; } else { name << "_a" << (1u << (uint32_t(aniso_filter) - 1)); } } if (aniso_filter == AnisoFilter::kDisabled || aniso_filter == AnisoFilter::kUseFetchConst) { static const char* kFilterSuffixes[] = {"p", "l", "b", "f"}; name << "_" << kFilterSuffixes[uint32_t(mag_filter)] << kFilterSuffixes[uint32_t(min_filter)] << kFilterSuffixes[uint32_t(mip_filter)]; } SamplerBinding new_sampler_binding; new_sampler_binding.fetch_constant = fetch_constant; new_sampler_binding.mag_filter = mag_filter; new_sampler_binding.min_filter = min_filter; new_sampler_binding.mip_filter = mip_filter; new_sampler_binding.aniso_filter = aniso_filter; new_sampler_binding.name = name.str(); uint32_t sampler_register = uint32_t(sampler_bindings_.size()); sampler_bindings_.emplace_back(std::move(new_sampler_binding)); return sampler_register; } void DxbcShaderTranslator::ArrayCoordToCubeDirection(uint32_t reg) { // This does the reverse of what the cube vector ALU instruction does, but // assuming S and T are normalized. // // The major axis depends on the face index (passed as a float in reg.z): // +X for 0, -X for 1, +Y for 2, -Y for 3, +Z for 4, -Z for 5. // // If the major axis is X: // * X is 1.0 or -1.0. // * Y is -T. // * Z is -S for positive X, +S for negative X. // If it's Y: // * X is +S. // * Y is 1.0 or -1.0. // * Z is +T for positive Y, -T for negative Y. // If it's Z: // * X is +S for positive Z, -S for negative Z. // * Y is -T. // * Z is 1.0 or -1.0. // Make 0, not 0.5, the center of S and T. // mad reg.xy__, reg.xy__, l(2.0, 2.0, _, _), l(-1.0, -1.0, _, _) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(15)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0011, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(reg); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0x40000000u); shader_code_.push_back(0x40000000u); shader_code_.push_back(0x3F800000u); shader_code_.push_back(0x3F800000u); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0xBF800000u); shader_code_.push_back(0xBF800000u); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.float_instruction_count; // Clamp the face index to 0...5 for safety (in case an offset was applied). // max reg.z, reg.z, l(0.0) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAX) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.float_instruction_count; // min reg.z, reg.z, l(5.0) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MIN) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0x40A00000); ++stat_.instruction_count; ++stat_.float_instruction_count; // Allocate a register for major axis info. uint32_t major_axis_temp = PushSystemTemp(); // Convert the face index to an integer. // ftou major_axis_temp.x, reg.z shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_FTOU) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(major_axis_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(reg); ++stat_.instruction_count; ++stat_.conversion_instruction_count; // Split the face number into major axis number and direction. // ubfe major_axis_temp.x__w, l(2, _, _, 1), l(1, _, _, 0), // major_axis_temp.x__x shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_UBFE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(15)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1001, 1)); shader_code_.push_back(major_axis_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(2); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(1); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(1); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(major_axis_temp); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Make booleans for whether each axis is major. // ieq major_axis_temp.xyz_, major_axis_temp.xxx_, l(0, 1, 2, _) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IEQ) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(major_axis_temp); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(major_axis_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(1); shader_code_.push_back(2); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.int_instruction_count; // Replace the face index in the source/destination with 1.0 or -1.0 for // swizzling. // movc reg.z, major_axis_temp.w, l(-1.0), l(1.0) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(major_axis_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0xBF800000u); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0x3F800000u); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Swizzle and negate the coordinates depending on which axis is major, but // don't negate according to the direction of the major axis (will be done // later). // X case. // movc reg.xyz_, major_axis_temp.xxx_, reg.zyx_, reg.xyz_ shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(major_axis_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b11000110, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(reg); ++stat_.instruction_count; ++stat_.movc_instruction_count; // movc reg._yz_, major_axis_temp._xx_, -reg._yz_, reg._yz_ shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0110, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(major_axis_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1) | ENCODE_D3D10_SB_OPERAND_EXTENDED(1)); shader_code_.push_back( ENCODE_D3D10_SB_EXTENDED_OPERAND_MODIFIER(D3D10_SB_OPERAND_MODIFIER_NEG)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(reg); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Y case. // movc reg._yz_, major_axis_temp._yy_, reg._zy_, reg._yz_ shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0110, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(major_axis_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b11011000, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(reg); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Z case. // movc reg.y, major_axis_temp.z, -reg.y, reg.y shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(major_axis_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1) | ENCODE_D3D10_SB_OPERAND_EXTENDED(1)); shader_code_.push_back( ENCODE_D3D10_SB_EXTENDED_OPERAND_MODIFIER(D3D10_SB_OPERAND_MODIFIER_NEG)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(reg); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Flip coordinates according to the direction of the major axis. // Z needs to be flipped if the major axis is X or Y, so make an X || Y mask. // X is flipped only when the major axis is Z. // or major_axis_temp.x, major_axis_temp.x, major_axis_temp.y shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_OR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(major_axis_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(major_axis_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(major_axis_temp); ++stat_.instruction_count; ++stat_.uint_instruction_count; // If the major axis is positive, nothing needs to be flipped. We have // 0xFFFFFFFF/0 at this point in the major axis mask, but 1/0 in the major // axis direction (didn't include W in ieq to waste less scalar operations), // but AND would result in 1/0, which is fine for movc too. // and major_axis_temp.x_z_, major_axis_temp.x_z_, major_axis_temp.w_w_ shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0101, 1)); shader_code_.push_back(major_axis_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(major_axis_temp); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(major_axis_temp); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Flip axes that need to be flipped. // movc reg.x_z_, major_axis_temp.z_x_, -reg.x_z_, reg.x_z_ shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0101, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b11000110, 1)); shader_code_.push_back(major_axis_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1) | ENCODE_D3D10_SB_OPERAND_EXTENDED(1)); shader_code_.push_back( ENCODE_D3D10_SB_EXTENDED_OPERAND_MODIFIER(D3D10_SB_OPERAND_MODIFIER_NEG)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(reg); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Release major_axis_temp. PopSystemTemp(); } void DxbcShaderTranslator::ProcessTextureFetchInstruction( const ParsedTextureFetchInstruction& instr) { if (FLAGS_dxbc_source_map) { instruction_disassembly_buffer_.Reset(); instr.Disassemble(&instruction_disassembly_buffer_); // Will be emitted later explicitly or by UpdateInstructionPredication. } // Predication should not affect derivative calculation: // https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/dx9-graphics-reference-asm-ps-registers-output-color // Do the part involving derivative calculation unconditionally, and re-enter // the predicate check before writing the result. bool suppress_predication = false; if (IsDXBCPixelShader()) { if (instr.opcode == FetchOpcode::kGetTextureComputedLod || instr.opcode == FetchOpcode::kGetTextureGradients) { suppress_predication = true; } else if (instr.opcode == FetchOpcode::kTextureFetch) { suppress_predication = instr.attributes.use_computed_lod && !instr.attributes.use_register_lod; } } uint32_t exec_p0_temp = UINT32_MAX; if (suppress_predication) { // Emit the disassembly before all this to indicate the reason of going // unconditional. EmitInstructionDisassembly(); // Close instruction-level predication. CloseInstructionPredication(); // Temporarily close exec-level predication - will reopen at the end, so not // changing cf_exec_predicated_. if (cf_exec_predicated_) { if (cf_exec_predicate_written_) { // Restore the predicate value in the beginning of the exec and put it // in exec_p0_temp. exec_p0_temp = PushSystemTemp(); // `if` case - the value was cf_exec_predicate_condition_. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(exec_p0_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(cf_exec_predicate_condition_ ? 0xFFFFFFFFu : 0u); ++stat_.instruction_count; ++stat_.mov_instruction_count; shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ELSE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; // `else` case - the value was !cf_exec_predicate_condition_. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(exec_p0_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(cf_exec_predicate_condition_ ? 0u : 0xFFFFFFFFu); ++stat_.instruction_count; ++stat_.mov_instruction_count; } shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; } } else { UpdateInstructionPredication(instr.is_predicated, instr.predicate_condition, true); } bool store_result = false; // Whether the result is only in X and all components should be remapped to X // while storing. bool replicate_result = false; DxbcSourceOperand operand; uint32_t operand_length = 0; if (instr.operand_count >= 1) { LoadDxbcSourceOperand(instr.operands[0], operand); operand_length = DxbcSourceOperandLength(operand); } uint32_t tfetch_index = instr.operands[1].storage_index; // Fetch constants are laid out like: // tf0[0] tf0[1] tf0[2] tf0[3] // tf0[4] tf0[5] tf1[0] tf1[1] // tf1[2] tf1[3] tf1[4] tf1[5] uint32_t tfetch_pair_offset = (tfetch_index >> 1) * 3; // TODO(Triang3l): kGetTextureBorderColorFrac. if (!IsDXBCPixelShader() && (instr.opcode == FetchOpcode::kGetTextureComputedLod || instr.opcode == FetchOpcode::kGetTextureGradients)) { // Quickly skip everything if tried to get anything involving derivatives // not in a pixel shader because only the pixel shader has derivatives. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(8)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.mov_instruction_count; } else if (instr.opcode == FetchOpcode::kTextureFetch || instr.opcode == FetchOpcode::kGetTextureComputedLod || instr.opcode == FetchOpcode::kGetTextureWeights) { store_result = true; // 0 is unsigned, 1 is signed. uint32_t srv_registers[2] = {UINT32_MAX, UINT32_MAX}; uint32_t srv_registers_stacked[2] = {UINT32_MAX, UINT32_MAX}; uint32_t sampler_register = UINT32_MAX; // Only the fetch constant needed for kGetTextureWeights. if (instr.opcode != FetchOpcode::kGetTextureWeights) { if (instr.opcode == FetchOpcode::kGetTextureComputedLod) { // The LOD is a scalar and it doesn't depend on the texture contents, so // require any variant - unsigned in this case because more texture // formats support it. srv_registers[0] = FindOrAddTextureSRV(tfetch_index, instr.dimension, false, true); if (instr.dimension == TextureDimension::k3D) { // 3D or 2D stacked is selected dynamically. srv_registers_stacked[0] = FindOrAddTextureSRV( tfetch_index, TextureDimension::k2D, false, true); } } else { srv_registers[0] = FindOrAddTextureSRV(tfetch_index, instr.dimension, false); srv_registers[1] = FindOrAddTextureSRV(tfetch_index, instr.dimension, true); if (instr.dimension == TextureDimension::k3D) { // 3D or 2D stacked is selected dynamically. srv_registers_stacked[0] = FindOrAddTextureSRV(tfetch_index, TextureDimension::k2D, false); srv_registers_stacked[1] = FindOrAddTextureSRV(tfetch_index, TextureDimension::k2D, true); } } sampler_register = FindOrAddSamplerBinding( tfetch_index, instr.attributes.mag_filter, instr.attributes.min_filter, instr.attributes.mip_filter, instr.attributes.aniso_filter); } uint32_t coord_temp = PushSystemTemp(); // Move coordinates to pv temporarily so zeros can be added to expand them // to Texture2DArray coordinates and to apply offset. Or, if the instruction // is getWeights, move them to pv because their fractional part will be // returned. uint32_t coord_mask = 0b0111; switch (instr.dimension) { case TextureDimension::k1D: coord_mask = 0b0001; break; case TextureDimension::k2D: coord_mask = 0b0011; break; case TextureDimension::k3D: coord_mask = 0b0111; break; case TextureDimension::kCube: // Don't need the 3rd component for getWeights because it's the face // index, so it doesn't participate in bilinear filtering. coord_mask = instr.opcode == FetchOpcode::kGetTextureWeights ? 0b0011 : 0b0111; break; } shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3 + operand_length)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, coord_mask, 1)); shader_code_.push_back(coord_temp); UseDxbcSourceOperand(operand); ++stat_.instruction_count; ++stat_.mov_instruction_count; // If 1D or 2D, fill the unused coordinates with zeros (sampling the only // row of the only slice). For getWeights, also clear the 4th component // because the coordinates will be returned. uint32_t coord_all_components_mask = instr.opcode == FetchOpcode::kGetTextureWeights ? 0b1111 : 0b0111; uint32_t coord_zero_mask = coord_all_components_mask & ~coord_mask; if (coord_zero_mask) { shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(8)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, coord_zero_mask, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.mov_instruction_count; } // Get the offset to see if the size of the texture is needed. // It's probably applicable to tfetchCube too, we're going to assume it's // used for them the same way as for stacked textures. // http://web.archive.org/web/20090511231340/http://msdn.microsoft.com:80/en-us/library/bb313959.aspx // Adding 1/1024 - quarter of one fixed-point unit of subpixel precision // (not to touch rounding when the GPU is converting to fixed-point) - to // resolve the ambiguity when the texture coordinate is directly between two // pixels, which hurts nearest-neighbor sampling (fixes the XBLA logo being // blocky in Banjo-Kazooie and the outlines around things and overall // blockiness in Halo 3). float offset_x = instr.attributes.offset_x; if (instr.opcode != FetchOpcode::kGetTextureWeights) { offset_x += 1.0f / 1024.0f; } float offset_y = 0.0f, offset_z = 0.0f; if (instr.dimension == TextureDimension::k2D || instr.dimension == TextureDimension::k3D || instr.dimension == TextureDimension::kCube) { offset_y = instr.attributes.offset_y; if (instr.opcode != FetchOpcode::kGetTextureWeights) { offset_y += 1.0f / 1024.0f; } // Don't care about the Z offset for cubemaps when getting weights because // zero Z will be returned anyway (the face index doesn't participate in // bilinear filtering). if (instr.dimension == TextureDimension::k3D || (instr.dimension == TextureDimension::kCube && instr.opcode != FetchOpcode::kGetTextureWeights)) { offset_z = instr.attributes.offset_z; if (instr.opcode != FetchOpcode::kGetTextureWeights && instr.dimension == TextureDimension::k3D) { // Z is the face index for cubemaps, so don't apply the epsilon to it. offset_z += 1.0f / 1024.0f; } } } // Get the texture size if needed, apply offset and switch between // normalized and unnormalized coordinates if needed. The offset is // fractional on the Xbox 360 (has 0.5 granularity), unlike in Direct3D 12, // and cubemaps possibly can have offset and their coordinates are different // than in Direct3D 12 (like an array texture rather than a direction). // getWeights instructions also need the texture size because they work like // frac(coord * texture_size). // TODO(Triang3l): Unnormalized coordinates should be disabled when the // wrap mode is not a clamped one, though it's probably a very rare case, // unlikely to be used on purpose. // http://web.archive.org/web/20090514012026/http://msdn.microsoft.com:80/en-us/library/bb313957.aspx uint32_t size_and_is_3d_temp = UINT32_MAX; // With 1/1024 this will always be true anyway, but let's keep the shorter // path without the offset in case some day this hack won't be used anymore // somehow. bool has_offset = offset_x != 0.0f || offset_y != 0.0f || offset_z != 0.0f; if (instr.opcode == FetchOpcode::kGetTextureWeights || has_offset || instr.attributes.unnormalized_coordinates || instr.dimension == TextureDimension::k3D) { size_and_is_3d_temp = PushSystemTemp(); // Will use fetch constants for the size. if (cbuffer_index_fetch_constants_ == kCbufferIndexUnallocated) { cbuffer_index_fetch_constants_ = cbuffer_count_++; } // Get 2D texture size and array layer count, in bits 0:12, 13:25, 26:31 // of dword 2 ([0].z or [2].x). shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_UBFE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(17)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(size_and_is_3d_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(13); shader_code_.push_back(instr.dimension != TextureDimension::k1D ? 13 : 0); shader_code_.push_back(instr.dimension == TextureDimension::k3D ? 6 : 0); shader_code_.push_back(0); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(13); shader_code_.push_back(26); shader_code_.push_back(0); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, 2 - 2 * (tfetch_index & 1), 3)); shader_code_.push_back(cbuffer_index_fetch_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kFetchConstants)); shader_code_.push_back(tfetch_pair_offset + (tfetch_index & 1) * 2); ++stat_.instruction_count; ++stat_.uint_instruction_count; if (instr.dimension == TextureDimension::k3D) { // Write whether the texture is 3D to W if it's 3D/stacked, as // 0xFFFFFFFF for 3D or 0 for stacked. The dimension is in dword 5 in // bits 9:10. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1000, 1)); shader_code_.push_back(size_and_is_3d_temp); // Dword 5 is [1].y or [2].w. shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, 1 + 2 * (tfetch_index & 1), 3)); shader_code_.push_back(cbuffer_index_fetch_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kFetchConstants)); shader_code_.push_back(tfetch_pair_offset + 1 + (tfetch_index & 1)); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0x3 << 9); ++stat_.instruction_count; ++stat_.uint_instruction_count; shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IEQ) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1000, 1)); shader_code_.push_back(size_and_is_3d_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(size_and_is_3d_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(uint32_t(Dimension::k3D) << 9); ++stat_.instruction_count; ++stat_.int_instruction_count; uint32_t size_3d_temp = PushSystemTemp(); // Get 3D texture size to a temporary variable (in the same constant, // but 11:11:10). shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_UBFE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(17)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(size_3d_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(11); shader_code_.push_back(11); shader_code_.push_back(10); shader_code_.push_back(0); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(11); shader_code_.push_back(22); shader_code_.push_back(0); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, 2 - 2 * (tfetch_index & 1), 3)); shader_code_.push_back(cbuffer_index_fetch_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kFetchConstants)); shader_code_.push_back(tfetch_pair_offset + (tfetch_index & 1) * 2); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Replace the 2D size with the 3D one if the texture is 3D. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(size_and_is_3d_temp); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(size_and_is_3d_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(size_3d_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(size_and_is_3d_temp); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Release size_3d_temp. PopSystemTemp(); } // Convert the size to float. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_UTOF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(size_and_is_3d_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(size_and_is_3d_temp); ++stat_.instruction_count; ++stat_.conversion_instruction_count; // Add 1 to the size because fetch constants store size minus one. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(size_and_is_3d_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(size_and_is_3d_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0x3F800000); shader_code_.push_back(0x3F800000); shader_code_.push_back(0x3F800000); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.float_instruction_count; if (instr.opcode == FetchOpcode::kGetTextureWeights) { // Weights for bilinear filtering - need to get the fractional part of // unnormalized coordinates. if (instr.attributes.unnormalized_coordinates) { if (has_offset) { // Apply the offset. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, coord_mask, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back( *reinterpret_cast(&offset_x)); shader_code_.push_back( *reinterpret_cast(&offset_y)); shader_code_.push_back( *reinterpret_cast(&offset_z)); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.float_instruction_count; } } else { // Unnormalize the coordinates and apply the offset. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(has_offset ? D3D10_SB_OPCODE_MAD : D3D10_SB_OPCODE_MUL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(has_offset ? 12 : 7)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, coord_mask, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(size_and_is_3d_temp); if (has_offset) { shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back( *reinterpret_cast(&offset_x)); shader_code_.push_back( *reinterpret_cast(&offset_y)); shader_code_.push_back( *reinterpret_cast(&offset_z)); shader_code_.push_back(0); } ++stat_.instruction_count; ++stat_.float_instruction_count; } if (instr.dimension == TextureDimension::k3D) { // Ignore Z if it's the texture is stacked - it's the array layer, so // there's no filtering across Z. Keep it only for 3D textures. This // assumes that the 3D/stacked flag is 0xFFFFFFFF or 0. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(size_and_is_3d_temp); ++stat_.instruction_count; ++stat_.uint_instruction_count; } } else { // Texture fetch - need to get normalized coordinates (with unnormalized // Z for stacked textures). if (instr.dimension == TextureDimension::k3D) { // Both 3D textures and 2D arrays have their Z coordinate normalized, // however, on PC, array elements have unnormalized indices. // https://www.slideshare.net/blackdevilvikas/next-generation-graphics-programming-on-xbox-360 // Put the array layer in W - Z * depth if the fetch uses normalized // coordinates, and Z if it uses unnormalized. if (instr.attributes.unnormalized_coordinates) { ++stat_.instruction_count; if (offset_z != 0.0f) { ++stat_.float_instruction_count; shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); } else { ++stat_.mov_instruction_count; shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); } shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, 0b1000, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(coord_temp); if (offset_z != 0.0f) { shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back( *reinterpret_cast(&offset_x)); } } else { if (offset_z != 0.0f) { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); } else { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MUL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); } shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, 0b1000, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(size_and_is_3d_temp); if (offset_z != 0.0f) { shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back( *reinterpret_cast(&offset_x)); } ++stat_.instruction_count; ++stat_.float_instruction_count; } } if (has_offset || instr.attributes.unnormalized_coordinates) { // Take the reciprocal of the size to normalize the coordinates and // the offset (this is not necessary to just sample 3D/array with // normalized coordinates and no offset). For cubemaps, there will be // 1 in Z, so this will work. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_RCP) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, coord_mask, 1)); shader_code_.push_back(size_and_is_3d_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(size_and_is_3d_temp); ++stat_.instruction_count; ++stat_.float_instruction_count; // Normalize the coordinates. if (instr.attributes.unnormalized_coordinates) { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MUL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, coord_mask, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(size_and_is_3d_temp); ++stat_.instruction_count; ++stat_.float_instruction_count; } // Apply the offset (coord = offset * 1/size + coord). if (has_offset) { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(12)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, coord_mask, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back( *reinterpret_cast(&offset_x)); shader_code_.push_back( *reinterpret_cast(&offset_y)); shader_code_.push_back( *reinterpret_cast(&offset_z)); shader_code_.push_back(0); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(size_and_is_3d_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(coord_temp); ++stat_.instruction_count; ++stat_.float_instruction_count; } } } } if (instr.opcode == FetchOpcode::kGetTextureWeights) { // Return the fractional part of unnormalized coordinates as bilinear // filtering weights. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_FRC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, coord_mask, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(coord_temp); ++stat_.instruction_count; ++stat_.float_instruction_count; } else { if (instr.dimension == TextureDimension::kCube) { // Convert cubemap coordinates passed as 2D array texture coordinates to // a 3D direction. We can't use a 2D array to emulate cubemaps because // at the edges, especially in pixel shader helper invocations, the // major axis changes, causing S/T to jump between 0 and 1, breaking // gradient calculation and causing the 1x1 mipmap to be sampled. ArrayCoordToCubeDirection(coord_temp); } // Bias the register LOD if fetching with explicit LOD (so this is not // done two or four times due to 3D/stacked and unsigned/signed). uint32_t lod_temp = system_temp_grad_h_lod_, lod_temp_component = 3; if (instr.opcode == FetchOpcode::kTextureFetch && instr.attributes.use_register_lod && instr.attributes.lod_bias != 0.0f) { lod_temp = PushSystemTemp(); lod_temp_component = 0; shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(lod_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(system_temp_grad_h_lod_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back( *reinterpret_cast(&instr.attributes.lod_bias)); ++stat_.instruction_count; ++stat_.float_instruction_count; } // Allocate the register for the value from the signed texture, and later // for biasing and gamma correction. uint32_t signs_value_temp = instr.opcode == FetchOpcode::kTextureFetch ? PushSystemTemp() : UINT32_MAX; // tfetch1D/2D/Cube just fetch directly. tfetch3D needs to fetch either // the 3D texture or the 2D stacked texture, so two sample instructions // selected conditionally are used in this case. if (instr.dimension == TextureDimension::k3D) { assert_true(size_and_is_3d_temp != UINT32_MAX); shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IF) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN( D3D10_SB_INSTRUCTION_TEST_NONZERO) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(size_and_is_3d_temp); ++stat_.instruction_count; ++stat_.dynamic_flow_control_count; } // Sample both 3D and 2D array bindings for tfetch3D. for (uint32_t i = 0; i < (instr.dimension == TextureDimension::k3D ? 2u : 1u); ++i) { if (i != 0) { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ELSE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; } // Sample both unsigned and signed. for (uint32_t j = 0; j < 2; ++j) { uint32_t srv_register_current = i != 0 ? srv_registers_stacked[j] : srv_registers[j]; uint32_t target_temp_current = j != 0 ? signs_value_temp : system_temp_pv_; if (instr.opcode == FetchOpcode::kGetTextureComputedLod) { // The non-pixel-shader case should be handled before because it // just returns a constant in this case. assert_true(IsDXBCPixelShader()); replicate_result = true; shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_1_SB_OPCODE_LOD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(11)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(target_temp_current); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_RESOURCE, kSwizzleXYZW, 2)); shader_code_.push_back(srv_register_current); shader_code_.push_back(srv_register_current); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_SAMPLER, kSwizzleXYZW, 2)); shader_code_.push_back(sampler_register); shader_code_.push_back(sampler_register); ++stat_.instruction_count; ++stat_.lod_instructions; // Apply the LOD bias if used. if (instr.attributes.lod_bias != 0.0f) { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(target_temp_current); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(target_temp_current); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(*reinterpret_cast( &instr.attributes.lod_bias)); ++stat_.instruction_count; ++stat_.float_instruction_count; } // In this case, only the unsigned variant is accessed because data // doesn't matter. break; } else if (instr.attributes.use_register_lod) { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_SAMPLE_L) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(13)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(target_temp_current); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_RESOURCE, kSwizzleXYZW, 2)); shader_code_.push_back(srv_register_current); shader_code_.push_back(srv_register_current); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_SAMPLER, kSwizzleXYZW, 2)); shader_code_.push_back(sampler_register); shader_code_.push_back(sampler_register); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_TEMP, lod_temp_component, 1)); shader_code_.push_back(lod_temp); ++stat_.instruction_count; ++stat_.texture_normal_instructions; } else if (instr.attributes.use_register_gradients) { // TODO(Triang3l): Apply the LOD bias somehow for register gradients // (possibly will require moving the bias to the sampler, which may // be not very good considering the sampler count is very limited). shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_SAMPLE_D) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(15)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(target_temp_current); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_RESOURCE, kSwizzleXYZW, 2)); shader_code_.push_back(srv_register_current); shader_code_.push_back(srv_register_current); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_SAMPLER, kSwizzleXYZW, 2)); shader_code_.push_back(sampler_register); shader_code_.push_back(sampler_register); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_grad_h_lod_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_grad_v_); ++stat_.instruction_count; ++stat_.texture_gradient_instructions; } else { // 3 different DXBC opcodes handled here: // - sample_l, when not using a computed LOD or not in a pixel // shader, in this case, LOD (0 + bias) is sampled. // - sample, when sampling in a pixel shader (thus with derivatives) // with a computed LOD. // - sample_b, when sampling in a pixel shader with a biased // computed LOD. // Both sample_l and sample_b should add the LOD bias as the last // operand in our case. bool explicit_lod = !instr.attributes.use_computed_lod || !IsDXBCPixelShader(); if (explicit_lod) { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_SAMPLE_L) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(13)); } else if (instr.attributes.lod_bias != 0.0f) { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_SAMPLE_B) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(13)); } else { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_SAMPLE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(11)); } shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(target_temp_current); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_RESOURCE, kSwizzleXYZW, 2)); shader_code_.push_back(srv_register_current); shader_code_.push_back(srv_register_current); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_SAMPLER, kSwizzleXYZW, 2)); shader_code_.push_back(sampler_register); shader_code_.push_back(sampler_register); if (explicit_lod || instr.attributes.lod_bias != 0.0f) { shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(*reinterpret_cast( &instr.attributes.lod_bias)); } ++stat_.instruction_count; if (!explicit_lod && instr.attributes.lod_bias != 0.0f) { ++stat_.texture_bias_instructions; } else { ++stat_.texture_normal_instructions; } } } } if (instr.dimension == TextureDimension::k3D) { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; } if (instr.opcode == FetchOpcode::kTextureFetch) { // Will take sign values and exponent bias from the fetch constant. if (cbuffer_index_fetch_constants_ == kCbufferIndexUnallocated) { cbuffer_index_fetch_constants_ = cbuffer_count_++; } assert_true(signs_value_temp != UINT32_MAX); uint32_t signs_temp = PushSystemTemp(); uint32_t signs_select_temp = PushSystemTemp(); // Multiplex unsigned and signed SRVs, apply sign bias (2 * color - 1) // and linearize gamma textures. This is done before applying the // exponent bias because biasing and linearization must be done on color // values in 0...1 range, and this is closer to the storage format, // while exponent bias is closer to the actual usage in shaders. // Extract the sign values from dword 0 ([0].x or [1].z) of the fetch // constant, in bits 2:3, 4:5, 6:7 and 8:9. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_UBFE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(17)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(signs_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(2); shader_code_.push_back(2); shader_code_.push_back(2); shader_code_.push_back(2); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(2); shader_code_.push_back(4); shader_code_.push_back(6); shader_code_.push_back(8); shader_code_.push_back(EncodeVectorReplicatedOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, (tfetch_index & 1) * 2, 3)); shader_code_.push_back(cbuffer_index_fetch_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kFetchConstants)); shader_code_.push_back(tfetch_pair_offset + (tfetch_index & 1)); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Replace the components fetched from the unsigned texture from those // fetched from the signed where needed (the signed values are already // loaded to signs_value_temp). shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IEQ) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(signs_select_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(signs_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(uint32_t(TextureSign::kSigned)); shader_code_.push_back(uint32_t(TextureSign::kSigned)); shader_code_.push_back(uint32_t(TextureSign::kSigned)); shader_code_.push_back(uint32_t(TextureSign::kSigned)); ++stat_.instruction_count; ++stat_.int_instruction_count; shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(signs_select_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(signs_value_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Reusing signs_value_temp from now because the value from the signed // texture has already been copied. // Expand 0...1 to -1...1 (for normal and DuDv maps, for instance). shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(15)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(signs_value_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0x40000000u); shader_code_.push_back(0x40000000u); shader_code_.push_back(0x40000000u); shader_code_.push_back(0x40000000u); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0xBF800000u); shader_code_.push_back(0xBF800000u); shader_code_.push_back(0xBF800000u); shader_code_.push_back(0xBF800000u); ++stat_.instruction_count; ++stat_.float_instruction_count; // Change the color to the biased one where needed. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IEQ) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(signs_select_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(signs_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(uint32_t(TextureSign::kUnsignedBiased)); shader_code_.push_back(uint32_t(TextureSign::kUnsignedBiased)); shader_code_.push_back(uint32_t(TextureSign::kUnsignedBiased)); shader_code_.push_back(uint32_t(TextureSign::kUnsignedBiased)); ++stat_.instruction_count; ++stat_.int_instruction_count; shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(signs_select_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(signs_value_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Linearize the texture if it's stored in a gamma format. for (uint32_t i = 0; i < 4; ++i) { // Calculate how far we are on each piece of the curve. Multiply by // 1/width of each piece, subtract start/width of it and saturate. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAD) | ENCODE_D3D10_SB_INSTRUCTION_SATURATE(1) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(15)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(signs_select_temp); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, i, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); // 1.0 / 0.25 shader_code_.push_back(0x40800000u); // 1.0 / 0.125 shader_code_.push_back(0x41000000u); // 1.0 / 0.375 shader_code_.push_back(0x402AAAABu); // 1.0 / 0.25 shader_code_.push_back(0x40800000u); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); // -0.0 / 0.25 shader_code_.push_back(0); // -0.25 / 0.125 shader_code_.push_back(0xC0000000u); // -0.375 / 0.375 shader_code_.push_back(0xBF800000u); // -0.75 / 0.25 shader_code_.push_back(0xC0400000u); ++stat_.instruction_count; ++stat_.float_instruction_count; // Combine the contribution of all pieces to the resulting linearized // value - multiply each piece by slope*width and sum them. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DP4) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1 << i, 1)); shader_code_.push_back(signs_value_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(signs_select_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); // 0.25 * 0.25 shader_code_.push_back(0x3D800000u); // 0.5 * 0.125 shader_code_.push_back(0x3D800000u); // 1.0 * 0.375 shader_code_.push_back(0x3EC00000u); // 2.0 * 0.25 shader_code_.push_back(0x3F000000u); ++stat_.instruction_count; ++stat_.float_instruction_count; } // Change the color to the linearized one where needed. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IEQ) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(signs_select_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(signs_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(uint32_t(TextureSign::kGamma)); shader_code_.push_back(uint32_t(TextureSign::kGamma)); shader_code_.push_back(uint32_t(TextureSign::kGamma)); shader_code_.push_back(uint32_t(TextureSign::kGamma)); ++stat_.instruction_count; ++stat_.int_instruction_count; shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(signs_select_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(signs_value_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Release signs_temp and signs_select_temp. PopSystemTemp(2); // Apply exponent bias. uint32_t exp_adjust_temp = PushSystemTemp(); // Get the bias value in bits 13:18 of dword 3, which is [0].w or [2].y. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_IBFE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(11)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(exp_adjust_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(6); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(13); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, 3 - 2 * (tfetch_index & 1), 3)); shader_code_.push_back(cbuffer_index_fetch_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kFetchConstants)); shader_code_.push_back(tfetch_pair_offset + (tfetch_index & 1) * 2); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Shift it into float exponent bits. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ISHL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(exp_adjust_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(exp_adjust_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(23); ++stat_.instruction_count; ++stat_.int_instruction_count; // Add this to the exponent of 1.0. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(exp_adjust_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(exp_adjust_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0x3F800000); ++stat_.instruction_count; ++stat_.int_instruction_count; // Multiply the value from the texture by 2.0^bias. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MUL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(exp_adjust_temp); ++stat_.instruction_count; ++stat_.float_instruction_count; // Release exp_adjust_temp. PopSystemTemp(); } if (signs_value_temp != UINT32_MAX) { PopSystemTemp(); } if (lod_temp != system_temp_grad_h_lod_) { PopSystemTemp(); } } if (size_and_is_3d_temp != UINT32_MAX) { PopSystemTemp(); } // Release coord_temp. PopSystemTemp(); } else if (instr.opcode == FetchOpcode::kGetTextureGradients) { assert_true(IsDXBCPixelShader()); store_result = true; // pv.xz = ddx(coord.xy) shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_DERIV_RTX_COARSE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3 + operand_length)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0101, 1)); shader_code_.push_back(system_temp_pv_); UseDxbcSourceOperand(operand, 0b01010000); ++stat_.instruction_count; ++stat_.float_instruction_count; // pv.yw = ddy(coord.xy) shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_DERIV_RTY_COARSE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3 + operand_length)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1010, 1)); shader_code_.push_back(system_temp_pv_); UseDxbcSourceOperand(operand, 0b01010000); ++stat_.instruction_count; ++stat_.float_instruction_count; // Get the exponent bias (horizontal in bits 22:26, vertical in bits 27:31 // of dword 4 ([1].x or [2].z) of the fetch constant). if (cbuffer_index_fetch_constants_ == kCbufferIndexUnallocated) { cbuffer_index_fetch_constants_ = cbuffer_count_++; } uint32_t exp_bias_temp = PushSystemTemp(); shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_IBFE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(17)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0011, 1)); shader_code_.push_back(exp_bias_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(5); shader_code_.push_back(5); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(22); shader_code_.push_back(27); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(EncodeVectorReplicatedOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, (tfetch_index & 1) * 2, 3)); shader_code_.push_back(cbuffer_index_fetch_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kFetchConstants)); shader_code_.push_back(tfetch_pair_offset + 1 + (tfetch_index & 1)); ++stat_.instruction_count; ++stat_.int_instruction_count; // Shift the exponent bias into float exponent bits. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ISHL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0011, 1)); shader_code_.push_back(exp_bias_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(exp_bias_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(23); shader_code_.push_back(23); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.int_instruction_count; // Add the bias to the exponent of 1.0. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0011, 1)); shader_code_.push_back(exp_bias_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(exp_bias_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0x3F800000); shader_code_.push_back(0x3F800000); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.int_instruction_count; // Apply the exponent bias. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MUL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b01000100, 1)); shader_code_.push_back(exp_bias_temp); ++stat_.instruction_count; ++stat_.float_instruction_count; // Release exp_bias_temp. PopSystemTemp(); } else if (instr.opcode == FetchOpcode::kSetTextureLod) { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3 + operand_length)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1000, 1)); shader_code_.push_back(system_temp_grad_h_lod_); UseDxbcSourceOperand(operand, kSwizzleXYZW, 0); ++stat_.instruction_count; ++stat_.mov_instruction_count; } else if (instr.opcode == FetchOpcode::kSetTextureGradientsHorz || instr.opcode == FetchOpcode::kSetTextureGradientsVert) { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3 + operand_length)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(instr.opcode == FetchOpcode::kSetTextureGradientsVert ? system_temp_grad_v_ : system_temp_grad_h_lod_); UseDxbcSourceOperand(operand); ++stat_.instruction_count; ++stat_.mov_instruction_count; } if (instr.operand_count >= 1) { UnloadDxbcSourceOperand(operand); } // Re-enter conditional execution if closed it. if (suppress_predication) { // Re-enter exec-level predication. if (cf_exec_predicated_) { D3D10_SB_INSTRUCTION_TEST_BOOLEAN test = cf_exec_predicate_condition_ ? D3D10_SB_INSTRUCTION_TEST_NONZERO : D3D10_SB_INSTRUCTION_TEST_ZERO; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IF) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN(test) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_TEMP, exec_p0_temp != UINT32_MAX ? 0 : 2, 1)); shader_code_.push_back( exec_p0_temp != UINT32_MAX ? exec_p0_temp : system_temp_ps_pc_p0_a0_); ++stat_.instruction_count; ++stat_.dynamic_flow_control_count; if (exec_p0_temp != UINT32_MAX) { PopSystemTemp(); } } // Update instruction-level predication to the one needed by this tfetch. UpdateInstructionPredication(instr.is_predicated, instr.predicate_condition, false); } if (store_result) { StoreResult(instr.result, system_temp_pv_, replicate_result); } } void DxbcShaderTranslator::ProcessVectorAluInstruction( const ParsedAluInstruction& instr) { if (FLAGS_dxbc_source_map) { instruction_disassembly_buffer_.Reset(); instr.Disassemble(&instruction_disassembly_buffer_); // Will be emitted by UpdateInstructionPredication. } UpdateInstructionPredication(instr.is_predicated, instr.predicate_condition, true); // Whether the instruction has changed the predicate and it needs to be // checked again later. bool predicate_written = false; // Whether the result is only in X and all components should be remapped to X // while storing. bool replicate_result = false; DxbcSourceOperand dxbc_operands[3]; uint32_t operand_length_sums[3]; for (uint32_t i = 0; i < uint32_t(instr.operand_count); ++i) { LoadDxbcSourceOperand(instr.operands[i], dxbc_operands[i]); operand_length_sums[i] = DxbcSourceOperandLength(dxbc_operands[i]); if (i != 0) { operand_length_sums[i] += operand_length_sums[i - 1]; } } // So the same code can be used for instructions with the same format. static const uint32_t kCoreOpcodes[] = { D3D10_SB_OPCODE_ADD, D3D10_SB_OPCODE_MUL, D3D10_SB_OPCODE_MAX, D3D10_SB_OPCODE_MIN, D3D10_SB_OPCODE_EQ, D3D10_SB_OPCODE_LT, D3D10_SB_OPCODE_GE, D3D10_SB_OPCODE_NE, D3D10_SB_OPCODE_FRC, D3D10_SB_OPCODE_ROUND_Z, D3D10_SB_OPCODE_ROUND_NI, D3D10_SB_OPCODE_MAD, D3D10_SB_OPCODE_EQ, D3D10_SB_OPCODE_GE, D3D10_SB_OPCODE_LT, D3D10_SB_OPCODE_DP4, D3D10_SB_OPCODE_DP3, D3D10_SB_OPCODE_DP2, 0, 0, D3D10_SB_OPCODE_EQ, D3D10_SB_OPCODE_NE, D3D10_SB_OPCODE_LT, D3D10_SB_OPCODE_GE, D3D10_SB_OPCODE_EQ, D3D10_SB_OPCODE_LT, D3D10_SB_OPCODE_GE, D3D10_SB_OPCODE_NE, 0, D3D10_SB_OPCODE_MAX, }; switch (instr.vector_opcode) { case AluVectorOpcode::kAdd: case AluVectorOpcode::kMax: // max is commonly used as mov, but probably better not to convert it to // make sure things like flusing denormals aren't affected. case AluVectorOpcode::kMin: shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE( kCoreOpcodes[uint32_t(instr.vector_opcode)]) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 3 + operand_length_sums[1])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(system_temp_pv_); UseDxbcSourceOperand(dxbc_operands[0]); UseDxbcSourceOperand(dxbc_operands[1]); ++stat_.instruction_count; ++stat_.float_instruction_count; break; case AluVectorOpcode::kMul: { shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MUL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 3 + operand_length_sums[1])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(system_temp_pv_); UseDxbcSourceOperand(dxbc_operands[0]); UseDxbcSourceOperand(dxbc_operands[1]); ++stat_.instruction_count; ++stat_.float_instruction_count; // Reproduce Shader Model 3 multiplication behavior (0 * anything = 0), // flushing denormals (must be done using eq - doing bitwise comparison // doesn't flush denormals). // With Shader Model 4 behavior, Halo 3 has a significant portion of the // image missing because rcp(0) is multiplied by 0, which results in NaN // rather than 0. uint32_t is_subnormal_temp = PushSystemTemp(); // Check the first operand. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_EQ) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 8 + operand_length_sums[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(is_subnormal_temp); UseDxbcSourceOperand(dxbc_operands[0]); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.float_instruction_count; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(12)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(is_subnormal_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Check the second operand. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_EQ) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 8 + DxbcSourceOperandLength(dxbc_operands[1]))); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(is_subnormal_temp); UseDxbcSourceOperand(dxbc_operands[1]); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.float_instruction_count; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(12)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(is_subnormal_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Release is_subnormal_temp. PopSystemTemp(); } break; case AluVectorOpcode::kSeq: case AluVectorOpcode::kSgt: case AluVectorOpcode::kSge: case AluVectorOpcode::kSne: shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE( kCoreOpcodes[uint32_t(instr.vector_opcode)]) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 3 + operand_length_sums[1])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(system_temp_pv_); if (instr.vector_opcode == AluVectorOpcode::kSgt) { // lt in DXBC, not gt. UseDxbcSourceOperand(dxbc_operands[1]); UseDxbcSourceOperand(dxbc_operands[0]); } else { UseDxbcSourceOperand(dxbc_operands[0]); UseDxbcSourceOperand(dxbc_operands[1]); } ++stat_.instruction_count; ++stat_.float_instruction_count; // Convert 0xFFFFFFFF to 1.0f. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0x3F800000); shader_code_.push_back(0x3F800000); shader_code_.push_back(0x3F800000); shader_code_.push_back(0x3F800000); ++stat_.instruction_count; ++stat_.uint_instruction_count; break; case AluVectorOpcode::kFrc: case AluVectorOpcode::kTrunc: case AluVectorOpcode::kFloor: shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE( kCoreOpcodes[uint32_t(instr.vector_opcode)]) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 3 + operand_length_sums[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(system_temp_pv_); UseDxbcSourceOperand(dxbc_operands[0]); ++stat_.instruction_count; ++stat_.float_instruction_count; break; case AluVectorOpcode::kMad: { shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 3 + operand_length_sums[2])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(system_temp_pv_); UseDxbcSourceOperand(dxbc_operands[0]); UseDxbcSourceOperand(dxbc_operands[1]); UseDxbcSourceOperand(dxbc_operands[2]); ++stat_.instruction_count; ++stat_.float_instruction_count; // Reproduce Shader Model 3 multiplication behavior (0 * anything = 0). // If any operand is zero or denormalized, just leave the addition part. uint32_t is_subnormal_temp = PushSystemTemp(); // Check the first operand. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_EQ) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 8 + operand_length_sums[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(is_subnormal_temp); UseDxbcSourceOperand(dxbc_operands[0]); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.float_instruction_count; shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 7 + DxbcSourceOperandLength(dxbc_operands[2]))); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(is_subnormal_temp); UseDxbcSourceOperand(dxbc_operands[2]); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Check the second operand. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_EQ) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 8 + DxbcSourceOperandLength(dxbc_operands[1]))); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(is_subnormal_temp); UseDxbcSourceOperand(dxbc_operands[1]); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.float_instruction_count; shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 7 + DxbcSourceOperandLength(dxbc_operands[2]))); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(is_subnormal_temp); UseDxbcSourceOperand(dxbc_operands[2]); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Release is_subnormal_temp. PopSystemTemp(); } break; // Using true eq to compare with zero because it handles denormals and -0. case AluVectorOpcode::kCndEq: case AluVectorOpcode::kCndGe: case AluVectorOpcode::kCndGt: // dest = src0 op 0.0 ? src1 : src2 // Compare src0 to zero. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE( kCoreOpcodes[uint32_t(instr.vector_opcode)]) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 8 + operand_length_sums[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(system_temp_pv_); if (instr.vector_opcode != AluVectorOpcode::kCndGt) { // lt in DXBC, not gt. UseDxbcSourceOperand(dxbc_operands[0]); } shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); if (instr.vector_opcode == AluVectorOpcode::kCndGt) { UseDxbcSourceOperand(dxbc_operands[0]); } ++stat_.instruction_count; ++stat_.float_instruction_count; // Select src1 or src2. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 5 + operand_length_sums[2] - operand_length_sums[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); UseDxbcSourceOperand(dxbc_operands[1]); UseDxbcSourceOperand(dxbc_operands[2]); ++stat_.instruction_count; ++stat_.movc_instruction_count; break; case AluVectorOpcode::kDp4: case AluVectorOpcode::kDp3: case AluVectorOpcode::kDp2Add: { uint32_t operand_mask; if (instr.vector_opcode == AluVectorOpcode::kDp2Add) { operand_mask = 0b0011; } else if (instr.vector_opcode == AluVectorOpcode::kDp3) { operand_mask = 0b0111; } else { operand_mask = 0b1111; } // Load the operands into pv and a temp register, zeroing if the other // operand is zero or denormalized, reproducing the Shader Model 3 // multiplication behavior (0 * anything = 0). uint32_t src1_temp = PushSystemTemp(); // Load the first operand into pv. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_EQ) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 8 + DxbcSourceOperandLength(dxbc_operands[1]))); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, operand_mask, 1)); shader_code_.push_back(system_temp_pv_); UseDxbcSourceOperand(dxbc_operands[1]); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.float_instruction_count; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 10 + operand_length_sums[0])); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, operand_mask, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); UseDxbcSourceOperand(dxbc_operands[0]); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Load the second operand into src1_temp. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_EQ) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 8 + operand_length_sums[0])); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, operand_mask, 1)); shader_code_.push_back(src1_temp); UseDxbcSourceOperand(dxbc_operands[0]); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.float_instruction_count; shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 10 + DxbcSourceOperandLength(dxbc_operands[1]))); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, operand_mask, 1)); shader_code_.push_back(src1_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(src1_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); UseDxbcSourceOperand(dxbc_operands[1]); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Calculate the dot product. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE( kCoreOpcodes[uint32_t(instr.vector_opcode)]) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(src1_temp); ++stat_.instruction_count; ++stat_.float_instruction_count; // Release src1_temp. PopSystemTemp(); // Add src2.x for dp2add. if (instr.vector_opcode == AluVectorOpcode::kDp2Add) { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 5 + DxbcSourceOperandLength(dxbc_operands[2]))); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); UseDxbcSourceOperand(dxbc_operands[2], kSwizzleXXXX); ++stat_.instruction_count; ++stat_.float_instruction_count; } break; } case AluVectorOpcode::kCube: { // 3D cubemap direction -> (T, S, 2.0 * major axis, face ID). // src0 is the direction swizzled as .zzxy, src1 is the same direction as // .yxzz, but we don't need it. // // If the major axis is X (X >= Y && X >= Z): // * T is -Y. // * S is -Z for positive X, +Z for negative X. // * Face is 0 for positive X, 1 for negative X. // Otherwise, if the major axis is Y (Y >= Z): // * T is +Z for positive Y, -Z for negative Y. // * S is +X. // * Face is 2 for positive Y, 3 for negative Y. // Otherwise, if the major axis is Z: // * T is -Y. // * S is +X for positive Z, -X for negative Z. // * Face is 4 for positive Z, 5 for negative Z. // For making swizzle masks when using src0. const uint32_t cube_src0_x = 2; const uint32_t cube_src0_y = 3; const uint32_t cube_src0_z = 1; // Used for various masks, as 0xFFFFFFFF/0, 2.0/0.0. uint32_t cube_mask_temp = PushSystemTemp(); // 1) Choose which axis is the major one - resulting in (0xFFFFFFFF, 0, 0) // for X major axis, (0, 0xFFFFFFFF, 0) for Y, (0, 0, 0xFFFFFFFF) for Z. // Mask = (X >= Y, Y >= Z, Z >= Z, X >= Z), let's hope nothing passes NaN // in Z. // ge mask, |src.xyzx|, |src.yzzz| shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_GE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 3 + 2 * DxbcSourceOperandLength(dxbc_operands[0], false, true))); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(cube_mask_temp); UseDxbcSourceOperand(dxbc_operands[0], cube_src0_x | (cube_src0_y << 2) | (cube_src0_z << 4) | (cube_src0_x << 6), 4, false, true); UseDxbcSourceOperand(dxbc_operands[0], cube_src0_y | (cube_src0_z << 2) | (cube_src0_z << 4) | (cube_src0_z << 6), 4, false, true); ++stat_.instruction_count; ++stat_.float_instruction_count; // Mask = (X >= Y && X >= Z, Y >= Z, Z >= Z, unused). // and mask.x, mask.x, mask.w shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(cube_mask_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(cube_mask_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(cube_mask_temp); ++stat_.instruction_count; ++stat_.uint_instruction_count; // If X is MA, Y and Z can't be MA. // movc mask._yz_, mask._xx_, l(_, 0, 0, _), mask._yz_ shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(12)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0110, 1)); shader_code_.push_back(cube_mask_temp); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(cube_mask_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(cube_mask_temp); ++stat_.instruction_count; ++stat_.movc_instruction_count; // If Y is MA, Z can't be MA. // movc mask.z, mask.y, l(0), mask.z shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1)); shader_code_.push_back(cube_mask_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(cube_mask_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(cube_mask_temp); ++stat_.instruction_count; ++stat_.movc_instruction_count; // 2) Get T and S as if the major axis was positive (sign changing for // negative major axis will be done later). uint32_t minus_src0_length = DxbcSourceOperandLength(dxbc_operands[0], true); // T is +Z if Y is major, -Y otherwise. // movc pv.x, mask.y, src.z, -src.y shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 5 + operand_length_sums[0] + minus_src0_length)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(cube_mask_temp); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, cube_src0_z); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, cube_src0_y, true); ++stat_.instruction_count; ++stat_.movc_instruction_count; // S is -Z if X is major, +X otherwise. // movc pv.y, mask.x, -src.z, src.x shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5 + minus_src0_length + operand_length_sums[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(cube_mask_temp); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, cube_src0_z, true); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, cube_src0_x); ++stat_.instruction_count; ++stat_.movc_instruction_count; // 3) Get 2.0 * major axis. // Convert the mask to float and double it (because we need 2 * MA). // and mask.xyz_, mask.xyz_, l(0x40000000, 0x40000000, 0x40000000, _) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(cube_mask_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(cube_mask_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0x40000000); shader_code_.push_back(0x40000000); shader_code_.push_back(0x40000000); shader_code_.push_back(0x40000000); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Select 2.0 * needed component (mask always has 2.0 in one component and // 0.0 in the rest). // dp3 pv.__z_, src.xyz_, mask.xyz_ shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DP3) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 5 + operand_length_sums[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1)); shader_code_.push_back(system_temp_pv_); UseDxbcSourceOperand(dxbc_operands[0], cube_src0_x | (cube_src0_y << 2) | (cube_src0_z << 4)); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(cube_mask_temp); ++stat_.instruction_count; ++stat_.float_instruction_count; // 4) Check whether the major axis is negative and get the face index. // Test if the major axis is negative. // lt mask.w, pv.z, l(0.0) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_LT) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1000, 1)); shader_code_.push_back(cube_mask_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.float_instruction_count; // Convert the negative mask to float the same way (multiplied by 2) // because it will be used in bitwise operations with other mask // components. // and mask.w, mask.w, l(0x40000000) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1000, 1)); shader_code_.push_back(cube_mask_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(cube_mask_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0x40000000); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Get the face index. If major axis is X, it's 0, if it's Y, it's 2, if // Z, it's 4, but also, being negative also adds 1 to the index. Since YZW // of the mask contain 2.0 for whether YZ are the major axis and the major // axis is negative, the factor is divided by 2. // dp3 pv.___w, mask.yzw_, l(1.0, 2.0, 0.5, _) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DP3) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1000, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, 0b11111001, 1)); shader_code_.push_back(cube_mask_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0x3F800000); shader_code_.push_back(0x40000000); shader_code_.push_back(0x3F000000); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.float_instruction_count; // 5) Flip axes if the major axis is negative - if major axis is Y, flip // T, otherwise flip S. // S needs to flipped if the major axis is X or Z, so make an X || Z mask. // or mask.x, mask.x, mask.z shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_OR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(cube_mask_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(cube_mask_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(cube_mask_temp); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Don't flip anything if the major axis is positive (AND 2.0 and 2.0 if // it's negative). // and mask.xy__, mask.xy__, mask.ww__ shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0011, 1)); shader_code_.push_back(cube_mask_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(cube_mask_temp); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(cube_mask_temp); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Flip T or S. // movc pv.xy__, mask.yx__, -pv.xy__, pv.xy__ shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0011, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, 0b11100001, 1)); shader_code_.push_back(cube_mask_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1) | ENCODE_D3D10_SB_OPERAND_EXTENDED(1)); shader_code_.push_back(ENCODE_D3D10_SB_EXTENDED_OPERAND_MODIFIER( D3D10_SB_OPERAND_MODIFIER_NEG)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); ++stat_.instruction_count; ++stat_.movc_instruction_count; // 6) Move T and S to the proper coordinate system. // Subtract abs(2.0 * major axis) from T and S. // add pv.xy__, pv.xy__, -|pv.zz__| shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(8)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0011, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1) | ENCODE_D3D10_SB_OPERAND_EXTENDED(1)); shader_code_.push_back(ENCODE_D3D10_SB_EXTENDED_OPERAND_MODIFIER( D3D10_SB_OPERAND_MODIFIER_ABSNEG)); shader_code_.push_back(system_temp_pv_); ++stat_.instruction_count; ++stat_.float_instruction_count; // Release cube_mask_temp. PopSystemTemp(); } break; case AluVectorOpcode::kMax4: replicate_result = true; // pv.xy = max(src0.xy, src0.zw) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAX) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 3 + 2 * operand_length_sums[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0011, 1)); shader_code_.push_back(system_temp_pv_); UseDxbcSourceOperand(dxbc_operands[0]); UseDxbcSourceOperand(dxbc_operands[0], 0b01001110); ++stat_.instruction_count; ++stat_.float_instruction_count; // pv.x = max(pv.x, pv.y) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAX) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(system_temp_pv_); ++stat_.instruction_count; ++stat_.float_instruction_count; break; case AluVectorOpcode::kSetpEqPush: case AluVectorOpcode::kSetpNePush: case AluVectorOpcode::kSetpGtPush: case AluVectorOpcode::kSetpGePush: predicate_written = true; replicate_result = true; // pv.xy = (src0.x == 0.0, src0.w == 0.0) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_EQ) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 8 + operand_length_sums[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0011, 1)); shader_code_.push_back(system_temp_pv_); UseDxbcSourceOperand(dxbc_operands[0], 0b11001100); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.float_instruction_count; // pv.zw = (src1.x op 0.0, src1.w op 0.0) shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE( kCoreOpcodes[uint32_t(instr.vector_opcode)]) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 8 + DxbcSourceOperandLength(dxbc_operands[1]))); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1100, 1)); shader_code_.push_back(system_temp_pv_); if (instr.vector_opcode != AluVectorOpcode::kSetpGtPush) { // lt in DXBC, not gt. UseDxbcSourceOperand(dxbc_operands[1], 0b11000000); } shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); if (instr.vector_opcode == AluVectorOpcode::kSetpGtPush) { UseDxbcSourceOperand(dxbc_operands[1], 0b11000000); } ++stat_.instruction_count; ++stat_.float_instruction_count; // p0 = src0.w == 0.0 && src1.w op 0.0 shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(system_temp_pv_); ++stat_.instruction_count; ++stat_.uint_instruction_count; // pv.x = src0.x == 0.0 && src1.x op 0.0 shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(system_temp_pv_); ++stat_.instruction_count; ++stat_.uint_instruction_count; // pv.x = (src0.x == 0.0 && src1.x op 0.0) ? -1.0 : src0.x // (1.0 is going to be added, thus -1.0) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 7 + operand_length_sums[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0xBF800000u); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 0); ++stat_.instruction_count; ++stat_.movc_instruction_count; // pv.x += 1.0 shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0x3F800000u); ++stat_.instruction_count; ++stat_.float_instruction_count; break; case AluVectorOpcode::kKillEq: case AluVectorOpcode::kKillGt: case AluVectorOpcode::kKillGe: case AluVectorOpcode::kKillNe: replicate_result = true; // pv = src0 op src1 shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE( kCoreOpcodes[uint32_t(instr.vector_opcode)]) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 3 + operand_length_sums[1])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(system_temp_pv_); if (instr.vector_opcode == AluVectorOpcode::kKillGt) { // lt in DXBC, not gt. UseDxbcSourceOperand(dxbc_operands[1]); UseDxbcSourceOperand(dxbc_operands[0]); } else { UseDxbcSourceOperand(dxbc_operands[0]); UseDxbcSourceOperand(dxbc_operands[1]); } ++stat_.instruction_count; ++stat_.float_instruction_count; // pv = any(src0 op src1) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_OR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0011, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, 0b01001110, 1)); shader_code_.push_back(system_temp_pv_); ++stat_.instruction_count; ++stat_.uint_instruction_count; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_OR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(system_temp_pv_); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Convert 0xFFFFFFFF to 1.0f. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0x3F800000); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Discard. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DISCARD) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN( D3D10_SB_INSTRUCTION_TEST_NONZERO) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_pv_); ++stat_.instruction_count; break; case AluVectorOpcode::kDst: { // Not shortening so there are no write-read dependencies and less scalar // operations. // pv.x = 1.0 shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0x3F800000); ++stat_.instruction_count; ++stat_.mov_instruction_count; // pv.y = src0.y * src1.y // Reproduce Shader Model 3 multiplication behavior (0 * anything = 0). // This is an attenuation calculation function, so infinity is probably // not very unlikely. uint32_t is_subnormal_temp = PushSystemTemp(); // Check if src0.y is zero. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_EQ) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 5 + operand_length_sums[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(is_subnormal_temp); UseDxbcSourceOperand(dxbc_operands[0]); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.float_instruction_count; // Check if src1.y is zero. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_EQ) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 5 + DxbcSourceOperandLength(dxbc_operands[1]))); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(is_subnormal_temp); UseDxbcSourceOperand(dxbc_operands[1]); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.float_instruction_count; // Check if any operand is zero. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_OR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(is_subnormal_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(is_subnormal_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(is_subnormal_temp); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Do the multiplication. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MUL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 3 + operand_length_sums[1])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(system_temp_pv_); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 1); UseDxbcSourceOperand(dxbc_operands[1], kSwizzleXYZW, 1); ++stat_.instruction_count; ++stat_.float_instruction_count; // Set pv.y to zero if any operand is zero. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(is_subnormal_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(system_temp_pv_); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Release is_subnormal_temp. PopSystemTemp(); // pv.z = src0.z shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 3 + operand_length_sums[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1)); shader_code_.push_back(system_temp_pv_); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 2); ++stat_.instruction_count; ++stat_.mov_instruction_count; // pv.w = src1.w shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 3 + DxbcSourceOperandLength(dxbc_operands[1]))); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1000, 1)); shader_code_.push_back(system_temp_pv_); UseDxbcSourceOperand(dxbc_operands[1], kSwizzleXYZW, 3); ++stat_.instruction_count; ++stat_.mov_instruction_count; } break; case AluVectorOpcode::kMaxA: // The `a0 = int(clamp(floor(src0.w + 0.5), -256.0, 255.0))` part. // // Using specifically floor(src0.w + 0.5) rather than round(src0.w) // because the R600 ISA reference and MSDN say so - this makes a // difference at 0.5 because round_ni rounds to the nearest even. // There's one deviation from the R600 specification though - the value is // clamped to 255 rather than set to -256 if it's over 255. We don't know // yet which is the correct - the mova_int description, for example, says // "clamp" explicitly. MSDN, however, says the value should actually be // clamped. // http://web.archive.org/web/20100705151335/http://msdn.microsoft.com:80/en-us/library/bb313931.aspx // // pv.x (temporary) = src0.w + 0.5 shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 5 + operand_length_sums[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_pv_); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 3); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0x3F000000u); ++stat_.instruction_count; ++stat_.float_instruction_count; // pv.x = floor(src0.w + 0.5) shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ROUND_NI) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_pv_); ++stat_.instruction_count; ++stat_.float_instruction_count; // pv.x = max(floor(src0.w + 0.5), -256.0) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAX) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0xC3800000u); ++stat_.instruction_count; ++stat_.float_instruction_count; // pv.x = clamp(floor(src0.w + 0.5), -256.0, 255.0) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MIN) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0x437F0000u); ++stat_.instruction_count; ++stat_.float_instruction_count; // a0 = int(clamp(floor(src0.w + 0.5), -256.0, 255.0)) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_FTOI) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1000, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_pv_); ++stat_.instruction_count; ++stat_.conversion_instruction_count; // The `pv = max(src0, src1)` part. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAX) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 3 + operand_length_sums[1])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(system_temp_pv_); UseDxbcSourceOperand(dxbc_operands[0]); UseDxbcSourceOperand(dxbc_operands[1]); ++stat_.instruction_count; ++stat_.float_instruction_count; break; default: assert_always(); // Unknown instruction - don't modify pv. break; } for (uint32_t i = 0; i < uint32_t(instr.operand_count); ++i) { UnloadDxbcSourceOperand(dxbc_operands[instr.operand_count - 1 - i]); } StoreResult(instr.result, system_temp_pv_, replicate_result); if (predicate_written) { cf_exec_predicate_written_ = true; CloseInstructionPredication(); } } void DxbcShaderTranslator::ProcessScalarAluInstruction( const ParsedAluInstruction& instr) { if (FLAGS_dxbc_source_map) { instruction_disassembly_buffer_.Reset(); instr.Disassemble(&instruction_disassembly_buffer_); // Will be emitted by UpdateInstructionPredication. } UpdateInstructionPredication(instr.is_predicated, instr.predicate_condition, true); // Whether the instruction has changed the predicate and it needs to be // checked again later. bool predicate_written = false; DxbcSourceOperand dxbc_operands[3]; uint32_t operand_lengths[3]; for (uint32_t i = 0; i < uint32_t(instr.operand_count); ++i) { LoadDxbcSourceOperand(instr.operands[i], dxbc_operands[i]); operand_lengths[i] = DxbcSourceOperandLength(dxbc_operands[i]); } // So the same code can be used for instructions with the same format. static const uint32_t kCoreOpcodes[] = { D3D10_SB_OPCODE_ADD, D3D10_SB_OPCODE_ADD, D3D10_SB_OPCODE_MUL, D3D10_SB_OPCODE_MUL, D3D10_SB_OPCODE_MUL, D3D10_SB_OPCODE_MAX, D3D10_SB_OPCODE_MIN, D3D10_SB_OPCODE_EQ, D3D10_SB_OPCODE_LT, D3D10_SB_OPCODE_GE, D3D10_SB_OPCODE_NE, D3D10_SB_OPCODE_FRC, D3D10_SB_OPCODE_ROUND_Z, D3D10_SB_OPCODE_ROUND_NI, D3D10_SB_OPCODE_EXP, D3D10_SB_OPCODE_LOG, D3D10_SB_OPCODE_LOG, D3D11_SB_OPCODE_RCP, D3D11_SB_OPCODE_RCP, D3D11_SB_OPCODE_RCP, D3D10_SB_OPCODE_RSQ, D3D10_SB_OPCODE_RSQ, D3D10_SB_OPCODE_RSQ, D3D10_SB_OPCODE_MAX, D3D10_SB_OPCODE_MAX, D3D10_SB_OPCODE_ADD, D3D10_SB_OPCODE_ADD, D3D10_SB_OPCODE_EQ, D3D10_SB_OPCODE_NE, D3D10_SB_OPCODE_LT, D3D10_SB_OPCODE_GE, 0, 0, 0, 0, D3D10_SB_OPCODE_EQ, D3D10_SB_OPCODE_LT, D3D10_SB_OPCODE_GE, D3D10_SB_OPCODE_NE, D3D10_SB_OPCODE_EQ, D3D10_SB_OPCODE_SQRT, 0, D3D10_SB_OPCODE_MUL, D3D10_SB_OPCODE_MUL, D3D10_SB_OPCODE_ADD, D3D10_SB_OPCODE_ADD, D3D10_SB_OPCODE_ADD, D3D10_SB_OPCODE_ADD, D3D10_SB_OPCODE_SINCOS, D3D10_SB_OPCODE_SINCOS, }; switch (instr.scalar_opcode) { case AluScalarOpcode::kAdds: case AluScalarOpcode::kMaxs: case AluScalarOpcode::kMins: case AluScalarOpcode::kSubs: { bool subtract = instr.scalar_opcode == AluScalarOpcode::kSubs; shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE( kCoreOpcodes[uint32_t(instr.scalar_opcode)]) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 3 + operand_lengths[0] + DxbcSourceOperandLength(dxbc_operands[0], subtract))); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 0); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 1, subtract); ++stat_.instruction_count; ++stat_.float_instruction_count; } break; case AluScalarOpcode::kAddsPrev: shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE( kCoreOpcodes[uint32_t(instr.scalar_opcode)]) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5 + operand_lengths[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 0); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); ++stat_.instruction_count; ++stat_.float_instruction_count; break; case AluScalarOpcode::kMuls: { // Reproduce Shader Model 3 multiplication behavior (0 * anything = 0). shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MUL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 3 + 2 * operand_lengths[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 0); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 1); ++stat_.instruction_count; ++stat_.float_instruction_count; // Check if the operands are zero or denormalized. uint32_t is_subnormal_temp = PushSystemTemp(); shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_EQ) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(8 + operand_lengths[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0011, 1)); shader_code_.push_back(is_subnormal_temp); UseDxbcSourceOperand(dxbc_operands[0]); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.float_instruction_count; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_OR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(is_subnormal_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(is_subnormal_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(is_subnormal_temp); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Set the result to zero if any operand is zero. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(is_subnormal_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Release is_subnormal_temp. PopSystemTemp(); } break; case AluScalarOpcode::kMulsPrev: { // Reproduce Shader Model 3 multiplication behavior (0 * anything = 0). uint32_t is_subnormal_temp = PushSystemTemp(); // Check if the first operand (src0.x) is zero. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_EQ) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5 + operand_lengths[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(is_subnormal_temp); UseDxbcSourceOperand(dxbc_operands[0]); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.float_instruction_count; // Check if the second operand (ps) is zero. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_EQ) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(is_subnormal_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.float_instruction_count; // Check if any operand is zero. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_OR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(is_subnormal_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(is_subnormal_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(is_subnormal_temp); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Do the multiplication. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MUL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5 + operand_lengths[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 0); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); ++stat_.instruction_count; ++stat_.float_instruction_count; // Set the result to zero if any operand is zero. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(is_subnormal_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Release is_subnormal_temp. PopSystemTemp(); } break; case AluScalarOpcode::kMulsPrev2: { // Implemented like MUL_LIT in the R600 ISA documentation, where src0 is // src0.x, src1 is ps, and src2 is src0.y. // Check if -FLT_MAX needs to be written - if any of the following // checks pass. uint32_t minus_max_mask = PushSystemTemp(); // ps == -FLT_MAX || ps == -Infinity (as ps <= -FLT_MAX) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_GE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(minus_max_mask); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0xFF7FFFFFu); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); ++stat_.instruction_count; ++stat_.float_instruction_count; // isnan(ps) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_NE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(minus_max_mask); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); ++stat_.instruction_count; ++stat_.float_instruction_count; // src0.y <= 0.0 shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_GE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5 + operand_lengths[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1)); shader_code_.push_back(minus_max_mask); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 1); ++stat_.instruction_count; ++stat_.float_instruction_count; // isnan(src0.y) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_NE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 3 + 2 * operand_lengths[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1000, 1)); shader_code_.push_back(minus_max_mask); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 1); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 1); ++stat_.instruction_count; ++stat_.float_instruction_count; // minus_max_mask = any(minus_max_mask) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_OR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0011, 1)); shader_code_.push_back(minus_max_mask); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(minus_max_mask); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, 0b01001110, 1)); shader_code_.push_back(minus_max_mask); ++stat_.instruction_count; ++stat_.uint_instruction_count; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_OR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(minus_max_mask); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(minus_max_mask); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(minus_max_mask); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Calculate the product for the regular path of the instruction. // ps = src0.x * ps shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MUL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5 + operand_lengths[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 0); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); ++stat_.instruction_count; ++stat_.float_instruction_count; // Write -FLT_MAX if needed. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(minus_max_mask); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0xFF7FFFFFu); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Release minus_max_mask. PopSystemTemp(); } break; case AluScalarOpcode::kSeqs: case AluScalarOpcode::kSgts: case AluScalarOpcode::kSges: case AluScalarOpcode::kSnes: shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE( kCoreOpcodes[uint32_t(instr.scalar_opcode)]) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5 + operand_lengths[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); if (instr.scalar_opcode != AluScalarOpcode::kSgts) { // lt in DXBC, not gt. UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 0); } shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0); if (instr.scalar_opcode == AluScalarOpcode::kSgts) { UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 0); } ++stat_.instruction_count; ++stat_.float_instruction_count; // Convert 0xFFFFFFFF to 1.0f. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0x3F800000); ++stat_.instruction_count; ++stat_.uint_instruction_count; break; case AluScalarOpcode::kFrcs: case AluScalarOpcode::kTruncs: case AluScalarOpcode::kFloors: case AluScalarOpcode::kExp: case AluScalarOpcode::kLog: case AluScalarOpcode::kRcp: case AluScalarOpcode::kRsq: case AluScalarOpcode::kSqrt: shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE( kCoreOpcodes[uint32_t(instr.scalar_opcode)]) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3 + operand_lengths[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 0); ++stat_.instruction_count; ++stat_.float_instruction_count; break; case AluScalarOpcode::kLogc: shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_LOG) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3 + operand_lengths[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 0); ++stat_.instruction_count; ++stat_.float_instruction_count; // Clamp -Infinity to -FLT_MAX. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAX) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0xFF7FFFFFu); ++stat_.instruction_count; ++stat_.float_instruction_count; break; case AluScalarOpcode::kRcpc: case AluScalarOpcode::kRsqc: shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE( kCoreOpcodes[uint32_t(instr.scalar_opcode)]) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3 + operand_lengths[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 0); ++stat_.instruction_count; ++stat_.float_instruction_count; // Clamp -Infinity to -FLT_MAX. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAX) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0xFF7FFFFFu); ++stat_.instruction_count; ++stat_.float_instruction_count; // Clamp +Infinity to +FLT_MAX. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MIN) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0x7F7FFFFFu); ++stat_.instruction_count; ++stat_.float_instruction_count; break; case AluScalarOpcode::kRcpf: case AluScalarOpcode::kRsqf: { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE( kCoreOpcodes[uint32_t(instr.scalar_opcode)]) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3 + operand_lengths[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 0); ++stat_.instruction_count; ++stat_.float_instruction_count; // Change Infinity to positive or negative zero (the sign of zero has // effect on some instructions, such as rcp itself). uint32_t isinf_and_sign = PushSystemTemp(); // Separate the value into the magnitude and the sign bit. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0011, 1)); shader_code_.push_back(isinf_and_sign); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0x7FFFFFFFu); shader_code_.push_back(0x80000000u); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Check if the magnitude is infinite. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IEQ) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(isinf_and_sign); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(isinf_and_sign); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0x7F800000u); ++stat_.instruction_count; ++stat_.int_instruction_count; // Zero ps if the magnitude is infinite (the signed zero is already in Y // of isinf_and_sign). shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(isinf_and_sign); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(isinf_and_sign); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Release isinf_and_sign. PopSystemTemp(); } break; case AluScalarOpcode::kMaxAs: case AluScalarOpcode::kMaxAsf: // The `a0 = int(clamp(round(src0.x), -256.0, 255.0))` part. // // See AluVectorOpcode::kMaxA handling for details regarding rounding and // clamping. // // a0 = round(src0.x) (towards the nearest integer via floor(src0.x + 0.5) // for maxas and towards -Infinity for maxasf). if (instr.scalar_opcode == AluScalarOpcode::kMaxAs) { // a0 = src0.x + 0.5 shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5 + operand_lengths[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1000, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 0); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0x3F000000u); ++stat_.instruction_count; ++stat_.float_instruction_count; // a0 = floor(src0.x + 0.5) shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ROUND_NI) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1000, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); ++stat_.instruction_count; ++stat_.float_instruction_count; } else { // a0 = floor(src0.x) shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ROUND_NI) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3 + operand_lengths[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1000, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 0); ++stat_.instruction_count; ++stat_.float_instruction_count; } // a0 = max(round(src0.x), -256.0) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAX) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1000, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0xC3800000u); ++stat_.instruction_count; ++stat_.float_instruction_count; // a0 = clamp(round(src0.x), -256.0, 255.0) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MIN) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1000, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0x437F0000u); ++stat_.instruction_count; ++stat_.float_instruction_count; // a0 = int(clamp(floor(src0.x + 0.5), -256.0, 255.0)) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_FTOI) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1000, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); ++stat_.instruction_count; ++stat_.conversion_instruction_count; // The `ps = max(src0.x, src0.y)` part. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAX) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 3 + 2 * operand_lengths[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 0); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 1); ++stat_.instruction_count; ++stat_.float_instruction_count; break; case AluScalarOpcode::kSubsPrev: shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE( kCoreOpcodes[uint32_t(instr.scalar_opcode)]) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(6 + operand_lengths[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 0); shader_code_.push_back( ENCODE_D3D10_SB_OPERAND_NUM_COMPONENTS(D3D10_SB_OPERAND_4_COMPONENT) | ENCODE_D3D10_SB_OPERAND_4_COMPONENT_SELECTION_MODE( D3D10_SB_OPERAND_4_COMPONENT_SELECT_1_MODE) | ENCODE_D3D10_SB_OPERAND_4_COMPONENT_SELECT_1(0) | ENCODE_D3D10_SB_OPERAND_TYPE(D3D10_SB_OPERAND_TYPE_TEMP) | ENCODE_D3D10_SB_OPERAND_INDEX_DIMENSION(D3D10_SB_OPERAND_INDEX_1D) | ENCODE_D3D10_SB_OPERAND_INDEX_REPRESENTATION( 0, D3D10_SB_OPERAND_INDEX_IMMEDIATE32) | ENCODE_D3D10_SB_OPERAND_EXTENDED(1)); shader_code_.push_back(ENCODE_D3D10_SB_EXTENDED_OPERAND_MODIFIER( D3D10_SB_OPERAND_MODIFIER_NEG)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); ++stat_.instruction_count; ++stat_.float_instruction_count; break; case AluScalarOpcode::kSetpEq: case AluScalarOpcode::kSetpNe: case AluScalarOpcode::kSetpGt: case AluScalarOpcode::kSetpGe: predicate_written = true; // Set p0 to whether the comparison with zero passes. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE( kCoreOpcodes[uint32_t(instr.scalar_opcode)]) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5 + operand_lengths[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); if (instr.scalar_opcode != AluScalarOpcode::kSetpGt) { // lt in DXBC, not gt. UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 0); } shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0); if (instr.scalar_opcode == AluScalarOpcode::kSetpGt) { UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 0); } ++stat_.instruction_count; ++stat_.float_instruction_count; // Set ps to 0.0 if the comparison passes or to 1.0 if it fails. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0x3F800000); ++stat_.instruction_count; ++stat_.movc_instruction_count; break; case AluScalarOpcode::kSetpInv: predicate_written = true; // Compare src0 to 0.0 (taking denormals into account, for instance) to // know what to set ps to in case src0 is not 1.0. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_EQ) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5 + operand_lengths[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 0); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.float_instruction_count; // Assuming src0 is not 1.0 (this case will be handled later), set ps to // src0, except when it's zero - in this case, set ps to 1.0. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7 + operand_lengths[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0x3F800000); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 0); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Set p0 to whether src0 is 1.0. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_EQ) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5 + operand_lengths[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 0); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0x3F800000); ++stat_.instruction_count; ++stat_.float_instruction_count; // If src0 is 1.0, set ps to zero. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); ++stat_.instruction_count; ++stat_.movc_instruction_count; break; case AluScalarOpcode::kSetpPop: predicate_written = true; // ps = src0 - 1.0 shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5 + operand_lengths[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 0); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0xBF800000u); ++stat_.instruction_count; ++stat_.float_instruction_count; // Set p0 to whether (src0 - 1.0) is 0.0 or smaller. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_GE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); ++stat_.instruction_count; ++stat_.float_instruction_count; // If (src0 - 1.0) is 0.0 or smaller, set ps to 0.0 (already has // (src0 - 1.0), so clamping to zero is enough). shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAX) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.float_instruction_count; break; case AluScalarOpcode::kSetpClr: predicate_written = true; // ps = FLT_MAX shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0x7F7FFFFF); ++stat_.instruction_count; ++stat_.mov_instruction_count; // p0 = false shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.mov_instruction_count; break; case AluScalarOpcode::kSetpRstr: predicate_written = true; // Copy src0 to ps. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3 + operand_lengths[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 0); ++stat_.instruction_count; ++stat_.mov_instruction_count; // Set p0 to whether src0 is zero. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_EQ) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.float_instruction_count; break; case AluScalarOpcode::kKillsEq: case AluScalarOpcode::kKillsGt: case AluScalarOpcode::kKillsGe: case AluScalarOpcode::kKillsNe: case AluScalarOpcode::kKillsOne: // ps = src0.x op 0.0 (or src0.x == 1.0 for kills_one) shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE( kCoreOpcodes[uint32_t(instr.scalar_opcode)]) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5 + operand_lengths[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); if (instr.scalar_opcode != AluScalarOpcode::kKillsGt) { // lt in DXBC, not gt. UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 0); } shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back( instr.scalar_opcode == AluScalarOpcode::kKillsOne ? 0x3F800000 : 0); if (instr.scalar_opcode == AluScalarOpcode::kKillsGt) { UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 0); } ++stat_.instruction_count; ++stat_.float_instruction_count; // Convert 0xFFFFFFFF to 1.0f. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0x3F800000); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Discard. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DISCARD) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN( D3D10_SB_INSTRUCTION_TEST_NONZERO) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); ++stat_.instruction_count; break; case AluScalarOpcode::kMulsc0: case AluScalarOpcode::kMulsc1: { // Reproduce Shader Model 3 multiplication behavior (0 * anything = 0). uint32_t is_subnormal_temp = PushSystemTemp(); // Check if the first operand (src0.x) is zero. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_EQ) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5 + operand_lengths[0])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(is_subnormal_temp); UseDxbcSourceOperand(dxbc_operands[0]); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.float_instruction_count; // Check if the second operand (src0.y) is zero. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_EQ) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5 + operand_lengths[1])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(is_subnormal_temp); UseDxbcSourceOperand(dxbc_operands[1]); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.float_instruction_count; // Check if any operand is zero. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_OR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(is_subnormal_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(is_subnormal_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(is_subnormal_temp); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Do the multiplication. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MUL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 3 + operand_lengths[0] + operand_lengths[1])); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 0); UseDxbcSourceOperand(dxbc_operands[1], kSwizzleXYZW, 0); ++stat_.instruction_count; ++stat_.float_instruction_count; // Set the result to zero if any operand is zero. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(is_subnormal_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Release is_subnormal_temp. PopSystemTemp(); } break; case AluScalarOpcode::kAddsc0: case AluScalarOpcode::kAddsc1: case AluScalarOpcode::kSubsc0: case AluScalarOpcode::kSubsc1: { bool subtract = instr.scalar_opcode == AluScalarOpcode::kSubsc0 || instr.scalar_opcode == AluScalarOpcode::kSubsc1; shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE( kCoreOpcodes[uint32_t(instr.scalar_opcode)]) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 3 + operand_lengths[0] + DxbcSourceOperandLength(dxbc_operands[1], subtract))); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 0); UseDxbcSourceOperand(dxbc_operands[1], kSwizzleXYZW, 0, subtract); ++stat_.instruction_count; ++stat_.float_instruction_count; } break; case AluScalarOpcode::kSin: case AluScalarOpcode::kCos: { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_SINCOS) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(4 + operand_lengths[0])); // sincos ps, null, src0.x for sin // sincos null, ps, src0.x for cos const uint32_t null_operand_token = ENCODE_D3D10_SB_OPERAND_NUM_COMPONENTS(D3D10_SB_OPERAND_0_COMPONENT) | ENCODE_D3D10_SB_OPERAND_TYPE(D3D10_SB_OPERAND_TYPE_NULL) | ENCODE_D3D10_SB_OPERAND_INDEX_DIMENSION(D3D10_SB_OPERAND_INDEX_0D); if (instr.scalar_opcode != AluScalarOpcode::kSin) { shader_code_.push_back(null_operand_token); } shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); if (instr.scalar_opcode != AluScalarOpcode::kCos) { shader_code_.push_back(null_operand_token); } UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 0); ++stat_.instruction_count; ++stat_.float_instruction_count; } break; default: // May be retain_prev, in this case the current ps should be written, or // something invalid that's better to ignore. assert_true(instr.scalar_opcode == AluScalarOpcode::kRetainPrev); break; } for (uint32_t i = 0; i < uint32_t(instr.operand_count); ++i) { UnloadDxbcSourceOperand(dxbc_operands[instr.operand_count - 1 - i]); } StoreResult(instr.result, system_temp_ps_pc_p0_a0_, true); if (predicate_written) { cf_exec_predicate_written_ = true; CloseInstructionPredication(); } } void DxbcShaderTranslator::ProcessAluInstruction( const ParsedAluInstruction& instr) { switch (instr.type) { case ParsedAluInstruction::Type::kNop: break; case ParsedAluInstruction::Type::kVector: ProcessVectorAluInstruction(instr); break; case ParsedAluInstruction::Type::kScalar: ProcessScalarAluInstruction(instr); break; } } uint32_t DxbcShaderTranslator::AppendString(std::vector& dest, const char* source) { size_t size = std::strlen(source) + 1; size_t size_aligned = xe::align(size, sizeof(uint32_t)); size_t dest_position = dest.size(); dest.resize(dest_position + size_aligned / sizeof(uint32_t)); std::memcpy(&dest[dest_position], source, size); // Don't leave uninitialized data, and make sure multiple invocations of the // translator for the same Xenos shader give the same DXBC. std::memset(reinterpret_cast(&dest[dest_position]) + size, 0xAB, size_aligned - size); return uint32_t(size_aligned); } const DxbcShaderTranslator::RdefType DxbcShaderTranslator::rdef_types_[size_t( DxbcShaderTranslator::RdefTypeIndex::kCount)] = { {"float", 0, 3, 1, 1, 0, 0, RdefTypeIndex::kUnknown, nullptr}, {"float2", 1, 3, 1, 2, 0, 0, RdefTypeIndex::kUnknown, nullptr}, {"float3", 1, 3, 1, 3, 0, 0, RdefTypeIndex::kUnknown, nullptr}, {"float4", 1, 3, 1, 4, 0, 0, RdefTypeIndex::kUnknown, nullptr}, {"int", 0, 2, 1, 1, 0, 0, RdefTypeIndex::kUnknown, nullptr}, {"uint", 0, 19, 1, 1, 0, 0, RdefTypeIndex::kUnknown, nullptr}, {"uint4", 1, 19, 1, 4, 0, 0, RdefTypeIndex::kUnknown, nullptr}, // Float constants - size written dynamically. {nullptr, 1, 3, 1, 4, 0, 0, RdefTypeIndex::kFloat4, nullptr}, {nullptr, 1, 19, 1, 4, 8, 0, RdefTypeIndex::kUint4, nullptr}, {nullptr, 1, 19, 1, 4, 32, 0, RdefTypeIndex::kUint4, nullptr}, {nullptr, 1, 19, 1, 4, 48, 0, RdefTypeIndex::kUint4, nullptr}, }; const DxbcShaderTranslator::SystemConstantRdef DxbcShaderTranslator:: system_constant_rdef_[DxbcShaderTranslator::kSysConst_Count] = { // vec4 0 {"xe_flags", RdefTypeIndex::kUint, 0, 4}, {"xe_vertex_index_endian", RdefTypeIndex::kUint, 4, 4}, {"xe_vertex_base_index", RdefTypeIndex::kUint, 8, 4}, {"xe_pixel_pos_reg", RdefTypeIndex::kUint, 12, 4}, // vec4 1 {"xe_ndc_scale", RdefTypeIndex::kFloat3, 16, 12}, {"xe_pixel_half_pixel_offset", RdefTypeIndex::kFloat, 28, 4}, // vec4 2 {"xe_ndc_offset", RdefTypeIndex::kFloat3, 32, 12}, {"xe_alpha_test", RdefTypeIndex::kInt, 44, 4}, // vec4 3 {"xe_point_size", RdefTypeIndex::kFloat2, 48, 8}, {"xe_point_size_min_max", RdefTypeIndex::kFloat2, 56, 8}, // vec4 4 {"xe_point_screen_to_ndc", RdefTypeIndex::kFloat2, 64, 8}, {"xe_ssaa_inv_scale", RdefTypeIndex::kFloat2, 72, 8}, // vec4 5 {"xe_alpha_test_range", RdefTypeIndex::kFloat2, 80, 8}, {"xe_edram_pitch_tiles", RdefTypeIndex::kUint, 88, 4}, {"xe_edram_depth_base_dwords", RdefTypeIndex::kUint, 92, 4}, // vec4 6 {"xe_color_exp_bias", RdefTypeIndex::kFloat4, 96, 16}, // vec4 7 {"xe_color_output_map", RdefTypeIndex::kUint4, 112, 16}, // vec4 8 {"xe_edram_stencil_reference", RdefTypeIndex::kUint, 128, 4}, {"xe_edram_stencil_read_mask", RdefTypeIndex::kUint, 132, 4}, {"xe_edram_stencil_write_mask", RdefTypeIndex::kUint, 136, 4}, // vec4 9 {"xe_edram_stencil_front", RdefTypeIndex::kUint4, 144, 16}, // vec4 10 {"xe_edram_stencil_back", RdefTypeIndex::kUint4, 160, 16}, // vec4 11 {"xe_edram_base_dwords", RdefTypeIndex::kUint4, 176, 16}, // vec4 12 {"xe_edram_rt_flags", RdefTypeIndex::kUint4, 192, 16}, // vec4 13 {"xe_edram_rt_pack_width_low", RdefTypeIndex::kUint4, 208, 16}, // vec4 14 {"xe_edram_rt_pack_offset_low", RdefTypeIndex::kUint4, 224, 16}, // vec4 15 {"xe_edram_rt_pack_width_high", RdefTypeIndex::kUint4, 240, 16}, // vec4 16 {"xe_edram_rt_pack_offset_high", RdefTypeIndex::kUint4, 256, 16}, // vec4 17 {"xe_edram_load_mask_low_rt01", RdefTypeIndex::kUint4, 272, 16}, // vec4 18 {"xe_edram_load_mask_low_rt23", RdefTypeIndex::kUint4, 288, 16}, // vec4 19 {"xe_edram_load_scale_rt01", RdefTypeIndex::kFloat4, 304, 16}, // vec4 20 {"xe_edram_load_scale_rt23", RdefTypeIndex::kFloat4, 320, 16}, // vec4 21 {"xe_edram_blend_rt01", RdefTypeIndex::kUint4, 336, 16}, // vec4 22 {"xe_edram_blend_rt23", RdefTypeIndex::kUint4, 352, 16}, // vec4 23 {"xe_edram_blend_constant", RdefTypeIndex::kFloat4, 368, 16}, // vec4 24 {"xe_edram_store_min_rt01", RdefTypeIndex::kFloat4, 384, 16}, // vec4 25 {"xe_edram_store_min_rt23", RdefTypeIndex::kFloat4, 400, 16}, // vec4 26 {"xe_edram_store_max_rt01", RdefTypeIndex::kFloat4, 416, 16}, // vec4 27 {"xe_edram_store_max_rt23", RdefTypeIndex::kFloat4, 432, 16}, // vec4 28 {"xe_edram_store_scale_rt01", RdefTypeIndex::kFloat4, 448, 16}, // vec4 29 {"xe_edram_store_scale_rt23", RdefTypeIndex::kFloat4, 464, 16}, }; void DxbcShaderTranslator::WriteResourceDefinitions() { // *************************************************************************** // Preparation // *************************************************************************** // Float constant count. uint32_t float_constant_count = 0; if (cbuffer_index_float_constants_ != kCbufferIndexUnallocated) { for (uint32_t i = 0; i < 4; ++i) { float_constant_count += xe::bit_count(constant_register_map().float_bitmap[i]); } } uint32_t chunk_position_dwords = uint32_t(shader_object_.size()); uint32_t new_offset; // *************************************************************************** // Header // *************************************************************************** // Constant buffer count. shader_object_.push_back(cbuffer_count_); // Constant buffer offset (set later). shader_object_.push_back(0); // Bound resource count (samplers, SRV, UAV, CBV). uint32_t resource_count = cbuffer_count_; if (!is_depth_only_pixel_shader_) { // + 1 for shared memory (vfetches can probably appear in pixel shaders too, // they are handled safely there anyway). resource_count += uint32_t(sampler_bindings_.size()) + 1 + uint32_t(texture_srvs_.size()); } if (IsDXBCPixelShader() && edram_rov_used_) { // EDRAM. ++resource_count; } shader_object_.push_back(resource_count); // Bound resource buffer offset (set later). shader_object_.push_back(0); if (IsDXBCVertexShader()) { // vs_5_1 shader_object_.push_back(0xFFFE0501u); } else { assert_true(IsDXBCPixelShader()); // ps_5_1 shader_object_.push_back(0xFFFF0501u); } // Compiler flags - default for SM 5.1 (no preshader, prefer flow control), // and also skip optimization and IEEE strictness. shader_object_.push_back(0x2504); // Generator offset (directly after the RDEF header in our case). shader_object_.push_back(60); // RD11, but with nibbles inverted (unlike in SM 5.0). shader_object_.push_back(0x25441313); // Unknown fields. shader_object_.push_back(60); shader_object_.push_back(24); // Was 32 in SM 5.0. shader_object_.push_back(40); shader_object_.push_back(40); shader_object_.push_back(36); shader_object_.push_back(12); shader_object_.push_back(0); // Generator name. AppendString(shader_object_, "Xenia"); // *************************************************************************** // Constant types // *************************************************************************** // Type names. new_offset = (uint32_t(shader_object_.size()) - chunk_position_dwords) * sizeof(uint32_t); uint32_t type_name_offsets[size_t(RdefTypeIndex::kCount)]; for (uint32_t i = 0; i < uint32_t(RdefTypeIndex::kCount); ++i) { const RdefType& type = rdef_types_[i]; if (type.name == nullptr) { // Array - use the name of the element type. type_name_offsets[i] = type_name_offsets[uint32_t(type.array_element_type)]; continue; } type_name_offsets[i] = new_offset; new_offset += AppendString(shader_object_, type.name); } // Types. uint32_t types_position_dwords = uint32_t(shader_object_.size()); const uint32_t type_size_dwords = 9; uint32_t types_offset = (types_position_dwords - chunk_position_dwords) * sizeof(uint32_t); const uint32_t type_size = type_size_dwords * sizeof(uint32_t); for (uint32_t i = 0; i < uint32_t(RdefTypeIndex::kCount); ++i) { const RdefType& type = rdef_types_[i]; shader_object_.push_back(type.type_class | (type.type << 16)); shader_object_.push_back(type.row_count | (type.column_count << 16)); if (RdefTypeIndex(i) == RdefTypeIndex::kFloat4ConstantArray) { // Declaring a 0-sized array may not be safe, so write something valid // even if they aren't used. shader_object_.push_back(std::max(float_constant_count, 1u)); } else { shader_object_.push_back(type.element_count | (type.struct_member_count << 16)); } // Struct member offset (set later). shader_object_.push_back(0); // Unknown. shader_object_.push_back(0); shader_object_.push_back(0); shader_object_.push_back(0); shader_object_.push_back(0); shader_object_.push_back(type_name_offsets[i]); } #if 0 // Structure members. Structures are not used currently, but were used in the // past, so the code is kept here. for (uint32_t i = 0; i < uint32_t(RdefTypeIndex::kCount); ++i) { const RdefType& type = rdef_types_[i]; const RdefStructMember* struct_members = type.struct_members; if (struct_members == nullptr) { continue; } uint32_t struct_member_position_dwords = uint32_t(shader_object_.size()); shader_object_[types_position_dwords + i * type_size_dwords + 3] = (struct_member_position_dwords - chunk_position_dwords) * sizeof(uint32_t); uint32_t struct_member_count = type.struct_member_count; // Reserve space for names and write types and offsets. for (uint32_t j = 0; j < struct_member_count; ++j) { shader_object_.push_back(0); shader_object_.push_back(types_offset + uint32_t(struct_members[j].type) * type_size); shader_object_.push_back(struct_members[j].offset); } // Write member names. new_offset = (uint32_t(shader_object_.size()) - chunk_position_dwords) * sizeof(uint32_t); for (uint32_t j = 0; j < struct_member_count; ++j) { shader_object_[struct_member_position_dwords + j * 3] = new_offset; new_offset += AppendString(shader_object_, struct_members[j].name); } } #endif // *************************************************************************** // Constants // *************************************************************************** // Names. new_offset = (uint32_t(shader_object_.size()) - chunk_position_dwords) * sizeof(uint32_t); uint32_t constant_name_offsets_system[kSysConst_Count]; if (cbuffer_index_system_constants_ != kCbufferIndexUnallocated) { for (uint32_t i = 0; i < kSysConst_Count; ++i) { constant_name_offsets_system[i] = new_offset; new_offset += AppendString(shader_object_, system_constant_rdef_[i].name); } } uint32_t constant_name_offset_float = new_offset; if (cbuffer_index_float_constants_ != kCbufferIndexUnallocated) { new_offset += AppendString(shader_object_, "xe_float_constants"); } uint32_t constant_name_offset_bool = new_offset; uint32_t constant_name_offset_loop = constant_name_offset_bool; if (cbuffer_index_bool_loop_constants_ != kCbufferIndexUnallocated) { new_offset += AppendString(shader_object_, "xe_bool_constants"); constant_name_offset_loop = new_offset; new_offset += AppendString(shader_object_, "xe_loop_constants"); } uint32_t constant_name_offset_fetch = new_offset; if (constant_name_offset_fetch != kCbufferIndexUnallocated) { new_offset += AppendString(shader_object_, "xe_fetch_constants"); } const uint32_t constant_size = 10 * sizeof(uint32_t); // System constants. uint32_t constant_offset_system = new_offset; if (cbuffer_index_system_constants_ != kCbufferIndexUnallocated) { for (uint32_t i = 0; i < kSysConst_Count; ++i) { const SystemConstantRdef& constant = system_constant_rdef_[i]; shader_object_.push_back(constant_name_offsets_system[i]); shader_object_.push_back(constant.offset); shader_object_.push_back(constant.size); // Flag 0x2 is D3D_SVF_USED. shader_object_.push_back((system_constants_used_ & (1ull << i)) ? 0x2 : 0); shader_object_.push_back(types_offset + uint32_t(constant.type) * type_size); // Default value (always 0). shader_object_.push_back(0); // Unknown. shader_object_.push_back(0xFFFFFFFFu); shader_object_.push_back(0); shader_object_.push_back(0xFFFFFFFFu); shader_object_.push_back(0); new_offset += constant_size; } } // Float constants. uint32_t constant_offset_float = new_offset; if (cbuffer_index_float_constants_ != kCbufferIndexUnallocated) { shader_object_.push_back(constant_name_offset_float); shader_object_.push_back(0); shader_object_.push_back(std::max(float_constant_count, 1u) * 4 * sizeof(float)); shader_object_.push_back(0x2); shader_object_.push_back(types_offset + uint32_t(RdefTypeIndex::kFloat4ConstantArray) * type_size); shader_object_.push_back(0); shader_object_.push_back(0xFFFFFFFFu); shader_object_.push_back(0); shader_object_.push_back(0xFFFFFFFFu); shader_object_.push_back(0); new_offset += constant_size; } // Bool and loop constants. uint32_t constant_offset_bool_loop = new_offset; if (cbuffer_index_bool_loop_constants_ != kCbufferIndexUnallocated) { shader_object_.push_back(constant_name_offset_bool); shader_object_.push_back(0); shader_object_.push_back(8 * 4 * sizeof(uint32_t)); shader_object_.push_back(0x2); shader_object_.push_back(types_offset + uint32_t(RdefTypeIndex::kUint4Array8) * type_size); shader_object_.push_back(0); shader_object_.push_back(0xFFFFFFFFu); shader_object_.push_back(0); shader_object_.push_back(0xFFFFFFFFu); shader_object_.push_back(0); new_offset += constant_size; shader_object_.push_back(constant_name_offset_loop); shader_object_.push_back(8 * 4 * sizeof(uint32_t)); shader_object_.push_back(32 * 4 * sizeof(uint32_t)); shader_object_.push_back(0x2); shader_object_.push_back( types_offset + uint32_t(RdefTypeIndex::kUint4Array32) * type_size); shader_object_.push_back(0); shader_object_.push_back(0xFFFFFFFFu); shader_object_.push_back(0); shader_object_.push_back(0xFFFFFFFFu); shader_object_.push_back(0); new_offset += constant_size; } // Fetch constants. uint32_t constant_offset_fetch = new_offset; if (cbuffer_index_fetch_constants_ != kCbufferIndexUnallocated) { shader_object_.push_back(constant_name_offset_fetch); shader_object_.push_back(0); shader_object_.push_back(32 * 6 * sizeof(uint32_t)); shader_object_.push_back(0x2); shader_object_.push_back( types_offset + uint32_t(RdefTypeIndex::kUint4Array48) * type_size); shader_object_.push_back(0); shader_object_.push_back(0xFFFFFFFFu); shader_object_.push_back(0); shader_object_.push_back(0xFFFFFFFFu); shader_object_.push_back(0); new_offset += constant_size; } // *************************************************************************** // Constant buffers // *************************************************************************** // Write the names. new_offset = (uint32_t(shader_object_.size()) - chunk_position_dwords) * sizeof(uint32_t); uint32_t cbuffer_name_offset_system = new_offset; if (cbuffer_index_system_constants_ != kCbufferIndexUnallocated) { new_offset += AppendString(shader_object_, "xe_system_cbuffer"); } uint32_t cbuffer_name_offset_float = new_offset; if (cbuffer_index_float_constants_ != kCbufferIndexUnallocated) { new_offset += AppendString(shader_object_, "xe_float_cbuffer"); } uint32_t cbuffer_name_offset_bool_loop = new_offset; if (cbuffer_index_bool_loop_constants_ != kCbufferIndexUnallocated) { new_offset += AppendString(shader_object_, "xe_bool_loop_cbuffer"); } uint32_t cbuffer_name_offset_fetch = new_offset; if (cbuffer_index_fetch_constants_ != kCbufferIndexUnallocated) { new_offset += AppendString(shader_object_, "xe_fetch_cbuffer"); } // Write the offset to the header. shader_object_[chunk_position_dwords + 1] = new_offset; // Write all the constant buffers, sorted by their binding index. for (uint32_t i = 0; i < cbuffer_count_; ++i) { if (i == cbuffer_index_system_constants_) { shader_object_.push_back(cbuffer_name_offset_system); shader_object_.push_back(kSysConst_Count); shader_object_.push_back(constant_offset_system); shader_object_.push_back( uint32_t(xe::align(sizeof(SystemConstants), 4 * sizeof(uint32_t)))); // D3D_CT_CBUFFER. shader_object_.push_back(0); // No D3D_SHADER_CBUFFER_FLAGS. shader_object_.push_back(0); } else if (i == cbuffer_index_float_constants_) { shader_object_.push_back(cbuffer_name_offset_float); shader_object_.push_back(1); shader_object_.push_back(constant_offset_float); shader_object_.push_back(std::max(float_constant_count, 1u) * 4 * sizeof(float)); shader_object_.push_back(0); shader_object_.push_back(0); } else if (i == cbuffer_index_bool_loop_constants_) { shader_object_.push_back(cbuffer_name_offset_bool_loop); // Bool constants and loop constants are separate for easier debugging. shader_object_.push_back(2); shader_object_.push_back(constant_offset_bool_loop); shader_object_.push_back((8 + 32) * 4 * sizeof(uint32_t)); shader_object_.push_back(0); shader_object_.push_back(0); } else if (i == cbuffer_index_fetch_constants_) { shader_object_.push_back(cbuffer_name_offset_fetch); shader_object_.push_back(1); shader_object_.push_back(constant_offset_fetch); shader_object_.push_back(32 * 6 * sizeof(uint32_t)); shader_object_.push_back(0); shader_object_.push_back(0); } } // *************************************************************************** // Bindings, in s#, t#, u#, cb# order // *************************************************************************** // Write used resource names, except for constant buffers because we have // their names already. new_offset = (uint32_t(shader_object_.size()) - chunk_position_dwords) * sizeof(uint32_t); uint32_t sampler_name_offset = 0; uint32_t shared_memory_name_offset = 0; uint32_t texture_name_offset = 0; if (!is_depth_only_pixel_shader_) { sampler_name_offset = new_offset; for (uint32_t i = 0; i < uint32_t(sampler_bindings_.size()); ++i) { new_offset += AppendString(shader_object_, sampler_bindings_[i].name.c_str()); } shared_memory_name_offset = new_offset; new_offset += AppendString(shader_object_, "xe_shared_memory"); texture_name_offset = new_offset; for (uint32_t i = 0; i < uint32_t(texture_srvs_.size()); ++i) { new_offset += AppendString(shader_object_, texture_srvs_[i].name.c_str()); } } uint32_t edram_name_offset = new_offset; if (IsDXBCPixelShader() && edram_rov_used_) { new_offset += AppendString(shader_object_, "xe_edram"); } // Write the offset to the header. shader_object_[chunk_position_dwords + 3] = new_offset; if (!is_depth_only_pixel_shader_) { // Samplers. for (uint32_t i = 0; i < uint32_t(sampler_bindings_.size()); ++i) { const SamplerBinding& sampler_binding = sampler_bindings_[i]; shader_object_.push_back(sampler_name_offset); // D3D_SIT_SAMPLER. shader_object_.push_back(3); // No D3D_RESOURCE_RETURN_TYPE. shader_object_.push_back(0); // D3D_SRV_DIMENSION_UNKNOWN (not an SRV). shader_object_.push_back(0); // Multisampling not applicable. shader_object_.push_back(0); // Register s[i]. shader_object_.push_back(i); // One binding. shader_object_.push_back(1); // No D3D_SHADER_INPUT_FLAGS. shader_object_.push_back(0); // Register space 0. shader_object_.push_back(0); // Sampler ID S[i]. shader_object_.push_back(i); sampler_name_offset += GetStringLength(sampler_binding.name.c_str()); } // Shared memory. shader_object_.push_back(shared_memory_name_offset); // D3D_SIT_BYTEADDRESS. shader_object_.push_back(7); // D3D_RETURN_TYPE_MIXED. shader_object_.push_back(6); // D3D_SRV_DIMENSION_BUFFER. shader_object_.push_back(1); // Multisampling not applicable. shader_object_.push_back(0); // Register t0. shader_object_.push_back(0); // One binding. shader_object_.push_back(1); // No D3D_SHADER_INPUT_FLAGS. shader_object_.push_back(0); // Register space 0. shader_object_.push_back(0); // SRV ID T0. shader_object_.push_back(0); for (uint32_t i = 0; i < uint32_t(texture_srvs_.size()); ++i) { const TextureSRV& texture_srv = texture_srvs_[i]; shader_object_.push_back(texture_name_offset); // D3D_SIT_TEXTURE. shader_object_.push_back(2); // D3D_RETURN_TYPE_FLOAT. shader_object_.push_back(5); switch (texture_srv.dimension) { case TextureDimension::k3D: // D3D_SRV_DIMENSION_TEXTURE3D. shader_object_.push_back(8); break; case TextureDimension::kCube: // D3D_SRV_DIMENSION_TEXTURECUBE. shader_object_.push_back(9); break; default: // D3D_SRV_DIMENSION_TEXTURE2DARRAY. shader_object_.push_back(5); } // Not multisampled. shader_object_.push_back(0xFFFFFFFFu); // Register t[1 + i] - t0 is shared memory. shader_object_.push_back(1 + i); // One binding. shader_object_.push_back(1); // D3D_SIF_TEXTURE_COMPONENTS (4-component). shader_object_.push_back(0xC); // Register space 0. shader_object_.push_back(0); // SRV ID T[1 + i] - T0 is shared memory. shader_object_.push_back(1 + i); texture_name_offset += GetStringLength(texture_srv.name.c_str()); } } if (IsDXBCPixelShader() && edram_rov_used_) { // EDRAM uint32 buffer. shader_object_.push_back(edram_name_offset); // D3D_SIT_UAV_RWTYPED. shader_object_.push_back(4); // D3D_RETURN_TYPE_UINT. shader_object_.push_back(4); // D3D_UAV_DIMENSION_BUFFER. shader_object_.push_back(1); // Not multisampled. shader_object_.push_back(0xFFFFFFFFu); // Register u0. shader_object_.push_back(0); // One binding. shader_object_.push_back(1); // No D3D_SHADER_INPUT_FLAGS. shader_object_.push_back(0); // Register space 0. shader_object_.push_back(0); // UAV ID U0. shader_object_.push_back(0); } // Constant buffers. for (uint32_t i = 0; i < cbuffer_count_; ++i) { uint32_t register_index = 0; if (i == cbuffer_index_system_constants_) { shader_object_.push_back(cbuffer_name_offset_system); register_index = uint32_t(CbufferRegister::kSystemConstants); } else if (i == cbuffer_index_float_constants_) { shader_object_.push_back(cbuffer_name_offset_float); register_index = uint32_t(CbufferRegister::kFloatConstants); } else if (i == cbuffer_index_bool_loop_constants_) { shader_object_.push_back(cbuffer_name_offset_bool_loop); register_index = uint32_t(CbufferRegister::kBoolLoopConstants); } else if (i == cbuffer_index_fetch_constants_) { shader_object_.push_back(cbuffer_name_offset_fetch); register_index = uint32_t(CbufferRegister::kFetchConstants); } // D3D_SIT_CBUFFER. shader_object_.push_back(0); // No D3D_RESOURCE_RETURN_TYPE. shader_object_.push_back(0); // D3D_SRV_DIMENSION_UNKNOWN (not an SRV). shader_object_.push_back(0); // Multisampling not applicable. shader_object_.push_back(0); shader_object_.push_back(register_index); // One binding. shader_object_.push_back(1); // D3D_SIF_USERPACKED if a `cbuffer` rather than a `ConstantBuffer`, but // we don't use indexable constant buffer descriptors. shader_object_.push_back(0); // Register space 0. shader_object_.push_back(0); // CBV ID CB[i]. shader_object_.push_back(i); } } void DxbcShaderTranslator::WriteInputSignature() { uint32_t chunk_position_dwords = uint32_t(shader_object_.size()); uint32_t new_offset; const uint32_t signature_position_dwords = 2; const uint32_t signature_size_dwords = 6; if (IsDXBCVertexShader()) { // Only unswapped vertex index. shader_object_.push_back(1); // Unknown. shader_object_.push_back(8); // Vertex index. // Semantic name SV_VertexID (the only one in the signature). shader_object_.push_back( (signature_position_dwords + signature_size_dwords) * sizeof(uint32_t)); // Semantic index. shader_object_.push_back(0); // D3D_NAME_VERTEX_ID. shader_object_.push_back(6); // D3D_REGISTER_COMPONENT_UINT32. shader_object_.push_back(1); shader_object_.push_back(uint32_t(InOutRegister::kVSInVertexIndex)); // x present, x used (always written to GPR 0). shader_object_.push_back(0x1 | (0x1 << 8)); // Vertex index semantic name. AppendString(shader_object_, "SV_VertexID"); } else { assert_true(IsDXBCPixelShader()); // Interpolators, point parameters (coordinates, size), screen position, // is front face. shader_object_.push_back(kInterpolatorCount + 3); // Unknown. shader_object_.push_back(8); // Intepolators. for (uint32_t i = 0; i < kInterpolatorCount; ++i) { // Reserve space for the semantic name (TEXCOORD). shader_object_.push_back(0); shader_object_.push_back(i); // D3D_NAME_UNDEFINED. shader_object_.push_back(0); // D3D_REGISTER_COMPONENT_FLOAT32. shader_object_.push_back(3); shader_object_.push_back(uint32_t(InOutRegister::kPSInInterpolators) + i); // Interpolators are copied to GPRs in the beginning of the shader. If // there's a register to copy to, this interpolator is used. uint32_t interpolator_used = (!is_depth_only_pixel_shader_ && i < register_count()) ? (0xF << 8) : 0; shader_object_.push_back(0xF | interpolator_used); } // Point parameters - coordinate on the point and point size as a float3 // TEXCOORD (but the size in Z is not needed). Always used because // ps_param_gen is handled dynamically. shader_object_.push_back(0); shader_object_.push_back(kPointParametersTexCoord); shader_object_.push_back(0); shader_object_.push_back(3); shader_object_.push_back(uint32_t(InOutRegister::kPSInPointParameters)); shader_object_.push_back(0x7 | (is_depth_only_pixel_shader_ ? 0 : (0x3 << 8))); // Position (only XY needed for ps_param_gen, but XYZ needed for ROV). // Always used because ps_param_gen is handled dynamically and because this // is needed for ROV storing. shader_object_.push_back(0); shader_object_.push_back(0); // D3D_NAME_POSITION. shader_object_.push_back(1); shader_object_.push_back(3); shader_object_.push_back(uint32_t(InOutRegister::kPSInPosition)); shader_object_.push_back(0xF | ((edram_rov_used_ ? 0x7 : 0x3) << 8)); // Is front face. Always used because ps_param_gen is handled dynamically. shader_object_.push_back(0); shader_object_.push_back(0); // D3D_NAME_IS_FRONT_FACE. shader_object_.push_back(9); shader_object_.push_back(1); shader_object_.push_back(uint32_t(InOutRegister::kPSInFrontFace)); if (edram_rov_used_) { shader_object_.push_back(0x1 | (0x1 << 8)); } else { shader_object_.push_back(0x1 | (is_depth_only_pixel_shader_ ? 0 : (0x1 << 8))); } // Write the semantic names. new_offset = (uint32_t(shader_object_.size()) - chunk_position_dwords) * sizeof(uint32_t); for (uint32_t i = 0; i < kInterpolatorCount + 1; ++i) { uint32_t texcoord_name_position_dwords = chunk_position_dwords + signature_position_dwords + i * signature_size_dwords; shader_object_[texcoord_name_position_dwords] = new_offset; } new_offset += AppendString(shader_object_, "TEXCOORD"); uint32_t position_name_position_dwords = chunk_position_dwords + signature_position_dwords + (kInterpolatorCount + 1) * signature_size_dwords; shader_object_[position_name_position_dwords] = new_offset; new_offset += AppendString(shader_object_, "SV_Position"); uint32_t front_face_name_position_dwords = position_name_position_dwords + signature_size_dwords; shader_object_[front_face_name_position_dwords] = new_offset; new_offset += AppendString(shader_object_, "SV_IsFrontFace"); } } void DxbcShaderTranslator::WriteOutputSignature() { uint32_t chunk_position_dwords = uint32_t(shader_object_.size()); uint32_t new_offset; const uint32_t signature_position_dwords = 2; const uint32_t signature_size_dwords = 6; if (IsDXBCVertexShader()) { // Interpolators, point parameters (coordinates, size), screen position. shader_object_.push_back(kInterpolatorCount + 2); // Unknown. shader_object_.push_back(8); // Intepolators. for (uint32_t i = 0; i < kInterpolatorCount; ++i) { // Reserve space for the semantic name (TEXCOORD). shader_object_.push_back(0); // Semantic index. shader_object_.push_back(i); // D3D_NAME_UNDEFINED. shader_object_.push_back(0); // D3D_REGISTER_COMPONENT_FLOAT32. shader_object_.push_back(3); shader_object_.push_back(uint32_t(InOutRegister::kVSOutInterpolators) + i); // Unlike in ISGN, the second byte contains the unused components, not the // used ones. All components are always used because they are reset to 0. shader_object_.push_back(0xF); } // Point parameters - coordinate on the point and point size as a float3 // TEXCOORD. Always used because reset to (0, 0, -1). shader_object_.push_back(0); shader_object_.push_back(kPointParametersTexCoord); shader_object_.push_back(0); shader_object_.push_back(3); shader_object_.push_back(uint32_t(InOutRegister::kVSOutPointParameters)); shader_object_.push_back(0x7 | (0x8 << 8)); // Position. shader_object_.push_back(0); shader_object_.push_back(0); // D3D_NAME_POSITION. shader_object_.push_back(1); shader_object_.push_back(3); shader_object_.push_back(uint32_t(InOutRegister::kVSOutPosition)); shader_object_.push_back(0xF); // Write the semantic names. new_offset = (uint32_t(shader_object_.size()) - chunk_position_dwords) * sizeof(uint32_t); for (uint32_t i = 0; i < kInterpolatorCount + 1; ++i) { uint32_t texcoord_name_position_dwords = chunk_position_dwords + signature_position_dwords + i * signature_size_dwords; shader_object_[texcoord_name_position_dwords] = new_offset; } new_offset += AppendString(shader_object_, "TEXCOORD"); uint32_t position_name_position_dwords = chunk_position_dwords + signature_position_dwords + (kInterpolatorCount + 1) * signature_size_dwords; shader_object_[position_name_position_dwords] = new_offset; new_offset += AppendString(shader_object_, "SV_Position"); } else { assert_true(IsDXBCPixelShader()); if (edram_rov_used_) { // No outputs - only ROV read/write. shader_object_.push_back(0); // Unknown. shader_object_.push_back(8); } else { // Color render targets, optionally depth. shader_object_.push_back((is_depth_only_pixel_shader_ ? 0 : 4) + (writes_depth_ ? 1 : 0)); // Unknown. shader_object_.push_back(8); // Color render targets. if (!is_depth_only_pixel_shader_) { for (uint32_t i = 0; i < 4; ++i) { // Reserve space for the semantic name (SV_Target). shader_object_.push_back(0); shader_object_.push_back(i); // D3D_NAME_UNDEFINED for some reason - this is correct. shader_object_.push_back(0); shader_object_.push_back(3); // Register must match the render target index. shader_object_.push_back(i); // All are used because X360 RTs are dynamically remapped to D3D12 RTs // to make the indices consecutive. shader_object_.push_back(0xF); } } // Depth. if (writes_depth_) { // Reserve space for the semantic name (SV_Depth). shader_object_.push_back(0); shader_object_.push_back(0); shader_object_.push_back(0); shader_object_.push_back(3); shader_object_.push_back(0xFFFFFFFFu); shader_object_.push_back(0x1 | (0xE << 8)); } // Write the semantic names. new_offset = (uint32_t(shader_object_.size()) - chunk_position_dwords) * sizeof(uint32_t); if (!is_depth_only_pixel_shader_) { for (uint32_t i = 0; i < 4; ++i) { uint32_t color_name_position_dwords = chunk_position_dwords + signature_position_dwords + i * signature_size_dwords; shader_object_[color_name_position_dwords] = new_offset; } } new_offset += AppendString(shader_object_, "SV_Target"); if (writes_depth_) { uint32_t depth_name_position_dwords = chunk_position_dwords + signature_position_dwords + 4 * signature_size_dwords; shader_object_[depth_name_position_dwords] = new_offset; new_offset += AppendString(shader_object_, "SV_Depth"); } } } } void DxbcShaderTranslator::WriteShaderCode() { uint32_t chunk_position_dwords = uint32_t(shader_object_.size()); shader_object_.push_back(ENCODE_D3D10_SB_TOKENIZED_PROGRAM_VERSION_TOKEN( IsDXBCVertexShader() ? D3D10_SB_VERTEX_SHADER : D3D10_SB_PIXEL_SHADER, 5, 1)); // Reserve space for the length token. shader_object_.push_back(0); // Declarations (don't increase the instruction count stat, and only inputs // and outputs are counted in dcl_count). // // Binding declarations have 3D-indexed operands with XYZW swizzle, the first // index being the binding ID (local to the shader), the second being the // lower register index bound, and the third being the highest register index // bound. Also dcl_ instructions for bindings are followed by the register // space index. // // Inputs/outputs have 1D-indexed operands with a component mask and a // register index. // Don't allow refactoring when converting to native code to maintain position // invariance (needed even in pixel shaders for oDepth invariance). shader_object_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_GLOBAL_FLAGS) | D3D11_1_SB_GLOBAL_FLAG_SKIP_OPTIMIZATION | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); // Constant buffers, from most frequenly accessed to least frequently accessed // (the order is a hint to the driver according to the DXBC header). if (cbuffer_index_float_constants_ != kCbufferIndexUnallocated) { uint32_t float_constant_count = 0; for (uint32_t i = 0; i < 4; ++i) { float_constant_count += xe::bit_count(constant_register_map().float_bitmap[i]); } shader_object_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_CONSTANT_BUFFER) | ENCODE_D3D10_SB_D3D10_SB_CONSTANT_BUFFER_ACCESS_PATTERN( float_constants_dynamic_indexed_ ? D3D10_SB_CONSTANT_BUFFER_DYNAMIC_INDEXED : D3D10_SB_CONSTANT_BUFFER_IMMEDIATE_INDEXED) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_object_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSwizzleXYZW, 3)); shader_object_.push_back(cbuffer_index_float_constants_); shader_object_.push_back(uint32_t(CbufferRegister::kFloatConstants)); shader_object_.push_back(uint32_t(CbufferRegister::kFloatConstants)); shader_object_.push_back(float_constant_count); shader_object_.push_back(0); } if (cbuffer_index_system_constants_ != kCbufferIndexUnallocated) { shader_object_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_CONSTANT_BUFFER) | ENCODE_D3D10_SB_D3D10_SB_CONSTANT_BUFFER_ACCESS_PATTERN( D3D10_SB_CONSTANT_BUFFER_IMMEDIATE_INDEXED) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_object_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSwizzleXYZW, 3)); shader_object_.push_back(cbuffer_index_system_constants_); shader_object_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_object_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_object_.push_back((sizeof(SystemConstants) + 15) >> 4); shader_object_.push_back(0); } if (cbuffer_index_fetch_constants_ != kCbufferIndexUnallocated) { shader_object_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_CONSTANT_BUFFER) | ENCODE_D3D10_SB_D3D10_SB_CONSTANT_BUFFER_ACCESS_PATTERN( D3D10_SB_CONSTANT_BUFFER_IMMEDIATE_INDEXED) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_object_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSwizzleXYZW, 3)); shader_object_.push_back(cbuffer_index_fetch_constants_); shader_object_.push_back(uint32_t(CbufferRegister::kFetchConstants)); shader_object_.push_back(uint32_t(CbufferRegister::kFetchConstants)); shader_object_.push_back(48); shader_object_.push_back(0); } if (cbuffer_index_bool_loop_constants_ != kCbufferIndexUnallocated) { shader_object_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_CONSTANT_BUFFER) | ENCODE_D3D10_SB_D3D10_SB_CONSTANT_BUFFER_ACCESS_PATTERN( bool_loop_constants_dynamic_indexed_ ? D3D10_SB_CONSTANT_BUFFER_DYNAMIC_INDEXED : D3D10_SB_CONSTANT_BUFFER_IMMEDIATE_INDEXED) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_object_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSwizzleXYZW, 3)); shader_object_.push_back(cbuffer_index_bool_loop_constants_); shader_object_.push_back(uint32_t(CbufferRegister::kBoolLoopConstants)); shader_object_.push_back(uint32_t(CbufferRegister::kBoolLoopConstants)); shader_object_.push_back(40); shader_object_.push_back(0); } // Samplers. for (uint32_t i = 0; i < uint32_t(sampler_bindings_.size()); ++i) { const SamplerBinding& sampler_binding = sampler_bindings_[i]; shader_object_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_SAMPLER) | ENCODE_D3D10_SB_SAMPLER_MODE(D3D10_SB_SAMPLER_MODE_DEFAULT) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(6)); shader_object_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_SAMPLER, kSwizzleXYZW, 3)); shader_object_.push_back(i); shader_object_.push_back(i); shader_object_.push_back(i); shader_object_.push_back(0); } // Shader resources. // Shared memory ByteAddressBuffer (T0, at t0, space0). shader_object_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_DCL_RESOURCE_RAW) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(6)); shader_object_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_RESOURCE, kSwizzleXYZW, 3)); shader_object_.push_back(0); shader_object_.push_back(0); shader_object_.push_back(0); shader_object_.push_back(0); // Textures. for (uint32_t i = 0; i < uint32_t(texture_srvs_.size()); ++i) { const TextureSRV& texture_srv = texture_srvs_[i]; D3D10_SB_RESOURCE_DIMENSION texture_srv_dimension; switch (texture_srv.dimension) { case TextureDimension::k3D: texture_srv_dimension = D3D10_SB_RESOURCE_DIMENSION_TEXTURE3D; break; case TextureDimension::kCube: texture_srv_dimension = D3D10_SB_RESOURCE_DIMENSION_TEXTURECUBE; break; default: texture_srv_dimension = D3D10_SB_RESOURCE_DIMENSION_TEXTURE2DARRAY; } shader_object_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_RESOURCE) | ENCODE_D3D10_SB_RESOURCE_DIMENSION(texture_srv_dimension) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_object_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_RESOURCE, kSwizzleXYZW, 3)); // T0 is shared memory. shader_object_.push_back(1 + i); // t0 is shared memory. shader_object_.push_back(1 + i); shader_object_.push_back(1 + i); shader_object_.push_back( ENCODE_D3D10_SB_RESOURCE_RETURN_TYPE(D3D10_SB_RETURN_TYPE_FLOAT, 0) | ENCODE_D3D10_SB_RESOURCE_RETURN_TYPE(D3D10_SB_RETURN_TYPE_FLOAT, 1) | ENCODE_D3D10_SB_RESOURCE_RETURN_TYPE(D3D10_SB_RETURN_TYPE_FLOAT, 2) | ENCODE_D3D10_SB_RESOURCE_RETURN_TYPE(D3D10_SB_RETURN_TYPE_FLOAT, 3)); shader_object_.push_back(0); } // Unordered access views. if (IsDXBCPixelShader() && edram_rov_used_) { // EDRAM uint32 rasterizer-ordered buffer (U0, at u0, space0). shader_object_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE( D3D11_SB_OPCODE_DCL_UNORDERED_ACCESS_VIEW_TYPED) | ENCODE_D3D10_SB_RESOURCE_DIMENSION(D3D10_SB_RESOURCE_DIMENSION_BUFFER) | D3D11_SB_RASTERIZER_ORDERED_ACCESS | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_object_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_RESOURCE, kSwizzleXYZW, 3)); shader_object_.push_back(0); shader_object_.push_back(0); shader_object_.push_back(0); shader_object_.push_back( ENCODE_D3D10_SB_RESOURCE_RETURN_TYPE(D3D10_SB_RETURN_TYPE_UINT, 0) | ENCODE_D3D10_SB_RESOURCE_RETURN_TYPE(D3D10_SB_RETURN_TYPE_UINT, 1) | ENCODE_D3D10_SB_RESOURCE_RETURN_TYPE(D3D10_SB_RETURN_TYPE_UINT, 2) | ENCODE_D3D10_SB_RESOURCE_RETURN_TYPE(D3D10_SB_RETURN_TYPE_UINT, 3)); shader_object_.push_back(0); } // Inputs and outputs. if (IsDXBCVertexShader()) { // Unswapped vertex index input (only X component). shader_object_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_INPUT_SGV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(4)); shader_object_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_INPUT, 0b0001, 1)); shader_object_.push_back(uint32_t(InOutRegister::kVSInVertexIndex)); shader_object_.push_back(ENCODE_D3D10_SB_NAME(D3D10_SB_NAME_VERTEX_ID)); ++stat_.dcl_count; // Interpolator output. for (uint32_t i = 0; i < kInterpolatorCount; ++i) { shader_object_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_OUTPUT) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_object_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_OUTPUT, 0b1111, 1)); shader_object_.push_back(uint32_t(InOutRegister::kVSOutInterpolators) + i); ++stat_.dcl_count; } // Point parameters output. shader_object_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_OUTPUT) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_object_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_OUTPUT, 0b0111, 1)); shader_object_.push_back(uint32_t(InOutRegister::kVSOutPointParameters)); ++stat_.dcl_count; // Position output. shader_object_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_OUTPUT_SIV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(4)); shader_object_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_OUTPUT, 0b1111, 1)); shader_object_.push_back(uint32_t(InOutRegister::kVSOutPosition)); shader_object_.push_back(ENCODE_D3D10_SB_NAME(D3D10_SB_NAME_POSITION)); ++stat_.dcl_count; } else if (IsDXBCPixelShader()) { // Interpolator input. if (!is_depth_only_pixel_shader_) { uint32_t interpolator_count = std::min(kInterpolatorCount, register_count()); for (uint32_t i = 0; i < interpolator_count; ++i) { shader_object_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_INPUT_PS) | ENCODE_D3D10_SB_INPUT_INTERPOLATION_MODE( D3D10_SB_INTERPOLATION_LINEAR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_object_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_INPUT, 0b1111, 1)); shader_object_.push_back(uint32_t(InOutRegister::kPSInInterpolators) + i); ++stat_.dcl_count; } // Point parameters input (only coordinates, not size, needed). shader_object_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_INPUT_PS) | ENCODE_D3D10_SB_INPUT_INTERPOLATION_MODE( D3D10_SB_INTERPOLATION_LINEAR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_object_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_INPUT, 0b0011, 1)); shader_object_.push_back(uint32_t(InOutRegister::kPSInPointParameters)); ++stat_.dcl_count; } // Position input (only XY needed for ps_param_gen, but for ROV access, XYZ // are needed). shader_object_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_INPUT_PS_SIV) | ENCODE_D3D10_SB_INPUT_INTERPOLATION_MODE( D3D10_SB_INTERPOLATION_LINEAR_NOPERSPECTIVE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(4)); shader_object_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_INPUT, edram_rov_used_ ? 0b0111 : 0b0011, 1)); shader_object_.push_back(uint32_t(InOutRegister::kPSInPosition)); shader_object_.push_back(ENCODE_D3D10_SB_NAME(D3D10_SB_NAME_POSITION)); ++stat_.dcl_count; if (edram_rov_used_ || !is_depth_only_pixel_shader_) { // Is front face. shader_object_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_INPUT_PS_SGV) | // This needs to be set according to FXC output, despite the // description in d3d12TokenizedProgramFormat.hpp saying bits 11:23 // are ignored. ENCODE_D3D10_SB_INPUT_INTERPOLATION_MODE( D3D10_SB_INTERPOLATION_CONSTANT) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(4)); shader_object_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_INPUT, 0b0001, 1)); shader_object_.push_back(uint32_t(InOutRegister::kPSInFrontFace)); shader_object_.push_back( ENCODE_D3D10_SB_NAME(D3D10_SB_NAME_IS_FRONT_FACE)); ++stat_.dcl_count; } if (!edram_rov_used_) { if (!is_depth_only_pixel_shader_) { // Color output. for (uint32_t i = 0; i < 4; ++i) { shader_object_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_OUTPUT) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_object_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_OUTPUT, 0b1111, 1)); shader_object_.push_back(i); ++stat_.dcl_count; } } // Depth output. if (writes_depth_) { shader_object_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_OUTPUT) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(2)); shader_object_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_OUTPUT_DEPTH, 0)); ++stat_.dcl_count; } } } // Temporary registers - guest general-purpose registers if not using dynamic // indexing and Xenia internal registers. stat_.temp_register_count = system_temp_count_max_; if (!is_depth_only_pixel_shader_ && !IndexableGPRsUsed()) { stat_.temp_register_count += register_count(); } if (stat_.temp_register_count != 0) { shader_object_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_TEMPS) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(2)); shader_object_.push_back(stat_.temp_register_count); } // General-purpose registers if using dynamic indexing (x0). if (!is_depth_only_pixel_shader_ && IndexableGPRsUsed()) { shader_object_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_INDEXABLE_TEMP) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(4)); // x0. shader_object_.push_back(0); shader_object_.push_back(register_count()); // 4 components in each. shader_object_.push_back(4); stat_.temp_array_count += register_count(); } // Initialize the depth output if used, which must be initialized on every // execution path. if (!edram_rov_used_ && IsDXBCPixelShader() && writes_depth_) { shader_object_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(4)); shader_object_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_OUTPUT_DEPTH, 0)); shader_object_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_object_.push_back(0); ++stat_.instruction_count; ++stat_.mov_instruction_count; } // Write the translated shader code. size_t code_size_dwords = shader_code_.size(); // So [] won't crash in case the size is zero somehow. if (code_size_dwords != 0) { shader_object_.resize(shader_object_.size() + code_size_dwords); std::memcpy(&shader_object_[shader_object_.size() - code_size_dwords], shader_code_.data(), code_size_dwords * sizeof(uint32_t)); } // Write the length. shader_object_[chunk_position_dwords + 1] = uint32_t(shader_object_.size()) - chunk_position_dwords; } } // namespace gpu } // namespace xe