/** ****************************************************************************** * 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 "third_party/dxbc/d3d12TokenizedProgramFormat.hpp" #include "xenia/base/assert.h" namespace xe { namespace gpu { using namespace ucode; 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; // A small shortcut, operands of cube are the same, but swizzled. uint32_t operand_count; if (instr.vector_opcode == AluVectorOpcode::kCube) { operand_count = 1; } else { operand_count = uint32_t(instr.operand_count); } DxbcSourceOperand dxbc_operands[3]; // Whether the operand is the same as any previous operand, and thus is loaded // only once. bool operands_duplicate[3] = {}; uint32_t operand_length_sums[3]; for (uint32_t i = 0; i < operand_count; ++i) { const InstructionOperand& operand = instr.operands[i]; for (uint32_t j = 0; j < i; ++j) { if (operand == instr.operands[j]) { operands_duplicate[i] = true; dxbc_operands[i] = dxbc_operands[j]; break; } } if (!operands_duplicate[i]) { LoadDxbcSourceOperand(operand, 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: shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ADD) | 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; if (!instr.operands[0].EqualsAbsolute(instr.operands[1])) { // 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(); // Get the non-NaN multiplicand closer to zero to check if any of them // is zero. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MIN) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 3 + DxbcSourceOperandLength(dxbc_operands[0], false, true) + DxbcSourceOperandLength(dxbc_operands[1], false, true))); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(is_subnormal_temp); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 4, false, true); UseDxbcSourceOperand(dxbc_operands[1], kSwizzleXYZW, 4, false, true); ++stat_.instruction_count; ++stat_.float_instruction_count; // Check if any multiplicand is zero (min isn't required to flush // denormals in the result). 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(is_subnormal_temp); 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); ++stat_.instruction_count; ++stat_.float_instruction_count; // Zero the result if any multiplicand is zero. 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::kMax: case AluVectorOpcode::kMin: // max is commonly used as mov. if (operands_duplicate[1]) { 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, 0b1111, 1)); shader_code_.push_back(system_temp_pv_); UseDxbcSourceOperand(dxbc_operands[0]); ++stat_.instruction_count; ++stat_.mov_instruction_count; } else { 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::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; if (!instr.operands[0].EqualsAbsolute(instr.operands[1])) { // 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(); // Get the non-NaN multiplicand closer to zero to check if any of them // is zero. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MIN) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 3 + DxbcSourceOperandLength(dxbc_operands[0], false, true) + DxbcSourceOperandLength(dxbc_operands[1], false, true))); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(is_subnormal_temp); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 4, false, true); UseDxbcSourceOperand(dxbc_operands[1], kSwizzleXYZW, 4, false, true); ++stat_.instruction_count; ++stat_.float_instruction_count; // Check if any multiplicand is zero (min isn't required to flush // denormals in the result). 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(is_subnormal_temp); 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); ++stat_.instruction_count; ++stat_.float_instruction_count; // Zero the multiplication part if any multiplicand is zero. 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: { if (instr.operands[0].EqualsAbsolute(instr.operands[1])) { // The operands are the same when calculating vector length, no need to // emulate 0 * anything = 0 in this case. 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; } else { 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 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; if (!instr.operands[0].EqualsAbsolute(instr.operands[1])) { // 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(); // Get the non-NaN multiplicand closer to zero to check if any of them // is zero. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MIN) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 3 + DxbcSourceOperandLength(dxbc_operands[0], false, true) + DxbcSourceOperandLength(dxbc_operands[1], false, true))); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(is_subnormal_temp); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 1, false, true); UseDxbcSourceOperand(dxbc_operands[1], kSwizzleXYZW, 1, false, true); ++stat_.instruction_count; ++stat_.float_instruction_count; // Check if any multiplicand is zero (min isn't required to flush // denormals in the result). 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, 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( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.float_instruction_count; // Set pv.y to zero if any multiplicand 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. if (operands_duplicate[1]) { 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, 0b1111, 1)); shader_code_.push_back(system_temp_pv_); UseDxbcSourceOperand(dxbc_operands[0]); ++stat_.instruction_count; ++stat_.mov_instruction_count; } else { 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 < operand_count; ++i) { uint32_t operand_index = operand_count - 1 - i; if (!operands_duplicate[operand_index]) { UnloadDxbcSourceOperand(dxbc_operands[operand_index]); } } StoreResult(instr.result, system_temp_pv_, replicate_result, instr.GetMemExportStreamConstant() != UINT32_MAX); 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]; // Whether the operand is the same as any previous operand, and thus is loaded // only once. bool operands_duplicate[3] = {}; uint32_t operand_lengths[3]; for (uint32_t i = 0; i < uint32_t(instr.operand_count); ++i) { const InstructionOperand& operand = instr.operands[i]; for (uint32_t j = 0; j < i; ++j) { if (operand == instr.operands[j]) { operands_duplicate[i] = true; dxbc_operands[i] = dxbc_operands[j]; break; } } if (!operands_duplicate[i]) { LoadDxbcSourceOperand(operand, 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::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: { 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; if (instr.operands[0].components[0] != instr.operands[0].components[1]) { // Reproduce Shader Model 3 multiplication behavior (0 * anything = 0). uint32_t is_subnormal_temp = PushSystemTemp(); // Get the non-NaN multiplicand closer to zero to check if any of them // is zero. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MIN) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 3 + 2 * DxbcSourceOperandLength(dxbc_operands[0], false, true))); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(is_subnormal_temp); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 0, false, true); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 1, false, true); ++stat_.instruction_count; ++stat_.float_instruction_count; // Check if any multiplicand is zero (min isn't required to flush // denormals in the result). 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, 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( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.float_instruction_count; // Zero the result if any multiplicand 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(); // Get the non-NaN multiplicand closer to zero to check if any of them is // zero. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MIN) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 6 + DxbcSourceOperandLength(dxbc_operands[0], false, true))); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(is_subnormal_temp); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 0, false, true); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1) | ENCODE_D3D10_SB_OPERAND_EXTENDED(1)); shader_code_.push_back(ENCODE_D3D10_SB_EXTENDED_OPERAND_MODIFIER( D3D10_SB_OPERAND_MODIFIER_ABS)); shader_code_.push_back(system_temp_ps_pc_p0_a0_); ++stat_.instruction_count; ++stat_.float_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; // Check if any multiplicand is zero (min isn't required to flush // denormals in the result). 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, 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( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.float_instruction_count; // Zero the result if any multiplicand 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::kMaxs: case AluScalarOpcode::kMins: { // max is commonly used as mov. if (instr.operands[0].components[0] == instr.operands[0].components[1]) { 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; } else { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE( kCoreOpcodes[uint32_t(instr.scalar_opcode)]) | 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::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. if (instr.operands[0].components[0] == instr.operands[0].components[1]) { 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; } else { 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( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 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_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: { 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; if (!instr.operands[0].EqualsAbsolute(instr.operands[1])) { // Reproduce Shader Model 3 multiplication behavior (0 * anything = 0). uint32_t is_subnormal_temp = PushSystemTemp(); // Get the non-NaN multiplicand closer to zero to check if any of them // is zero. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MIN) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 3 + DxbcSourceOperandLength(dxbc_operands[0], false, true) + DxbcSourceOperandLength(dxbc_operands[1], false, true))); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(is_subnormal_temp); UseDxbcSourceOperand(dxbc_operands[0], kSwizzleXYZW, 0, false, true); UseDxbcSourceOperand(dxbc_operands[1], kSwizzleXYZW, 0, false, true); ++stat_.instruction_count; ++stat_.float_instruction_count; // Check if any multiplicand is zero (min isn't required to flush // denormals in the result). 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, 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( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.float_instruction_count; // Zero the result if any multiplicand 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 if (instr.scalar_opcode != AluScalarOpcode::kSin) { shader_code_.push_back( EncodeZeroComponentOperand(D3D10_SB_OPERAND_TYPE_NULL, 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::kCos) { shader_code_.push_back( EncodeZeroComponentOperand(D3D10_SB_OPERAND_TYPE_NULL, 0)); } 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; } } } // namespace gpu } // namespace xe