/** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2018 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #include "xenia/gpu/dxbc_shader_translator.h" #include #include #include #include "third_party/dxbc/d3d12TokenizedProgramFormat.hpp" #include "xenia/base/assert.h" #include "xenia/base/string.h" namespace xe { namespace gpu { using namespace ucode; void DxbcShaderTranslator::SwapVertexData(uint32_t vfetch_index, uint32_t write_mask) { // Make sure we have fetch constants. if (cbuffer_index_fetch_constants_ == kCbufferIndexUnallocated) { cbuffer_index_fetch_constants_ = cbuffer_count_++; } // Allocate temporary registers for intermediate values. uint32_t temp1 = PushSystemTemp(); uint32_t temp2 = PushSystemTemp(); // 8-in-16: Create the value being built in temp1. // ushr temp1, pv, l(8, 8, 8, 8) // pv: ABCD, temp1: BCD0 shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_USHR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, write_mask, 1)); shader_code_.push_back(temp1); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(8); shader_code_.push_back(8); shader_code_.push_back(8); shader_code_.push_back(8); ++stat_.instruction_count; ++stat_.uint_instruction_count; // 8-in-16: Insert A in Y of temp1. // bfi temp1, l(8, 8, 8, 8), l(8, 8, 8, 8), pv, temp1 // pv: ABCD, temp1: BAD0 shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_BFI) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(17)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, write_mask, 1)); shader_code_.push_back(temp1); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(8); shader_code_.push_back(8); shader_code_.push_back(8); shader_code_.push_back(8); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(8); shader_code_.push_back(8); shader_code_.push_back(8); shader_code_.push_back(8); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(temp1); ++stat_.instruction_count; ++stat_.uint_instruction_count; // 8-in-16: Create the source for C insertion in temp2. // ushr temp2, pv, l(16, 16, 16, 16) // pv: ABCD, temp1: BAD0, temp2: CD00 shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_USHR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, write_mask, 1)); shader_code_.push_back(temp2); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(16); shader_code_.push_back(16); shader_code_.push_back(16); shader_code_.push_back(16); ++stat_.instruction_count; ++stat_.uint_instruction_count; // 8-in-16: Insert C in W of temp1. // bfi temp1, l(8, 8, 8, 8), l(24, 24, 24, 24), temp2, temp1 // pv: ABCD, temp1: BADC shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_BFI) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(17)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, write_mask, 1)); shader_code_.push_back(temp1); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(8); shader_code_.push_back(8); shader_code_.push_back(8); shader_code_.push_back(8); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(24); shader_code_.push_back(24); shader_code_.push_back(24); shader_code_.push_back(24); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(temp2); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(temp1); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Get bits indicating what swaps should be done. The endianness is located in // the low 2 bits of the second dword of the fetch constant: // - 00 for no swap. // - 01 for 8-in-16. // - 10 for 8-in-32 (8-in-16 and 16-in-32). // - 11 for 16-in-32. // ubfe temp2.xy, l(1, 1), l(0, 1), fetch.yy // pv: ABCD, temp1: BADC, temp2: 8in16/16in32?|8in32/16in32? shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_UBFE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(17)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0011, 1)); shader_code_.push_back(temp2); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(1); shader_code_.push_back(1); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(1); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(EncodeVectorReplicatedOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, (vfetch_index & 1) * 2 + 1, 3)); shader_code_.push_back(cbuffer_index_fetch_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kFetchConstants)); shader_code_.push_back(vfetch_index >> 1); ++stat_.instruction_count; ++stat_.uint_instruction_count; // 16-in-32 is used as intermediate swapping step here rather than 8-in-32. // Thus 8-in-16 needs to be done for 8-in-16 (01) and 8-in-32 (10). // And 16-in-32 needs to be done for 8-in-32 (10) and 16-in-32 (11). // xor temp2.x, temp2.x, temp2.y // pv: ABCD, temp1: BADC, temp2: 8in16/8in32?|8in32/16in32? shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_XOR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(temp2); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(temp2); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(temp2); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Write the 8-in-16 value to pv if needed. // movc pv, temp2.xxxx, temp1, pv // pv: ABCD/BADC, temp2: 8in16/8in32?|8in32/16in32? shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, write_mask, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(temp2); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(temp1); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); ++stat_.instruction_count; ++stat_.movc_instruction_count; // 16-in-32: Write the low 16 bits to temp1. // ushr temp1, pv, l(16, 16, 16, 16) // pv: ABCD/BADC, temp1: CD00/DC00, temp2: 8in16/8in32?|8in32/16in32? shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_USHR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, write_mask, 1)); shader_code_.push_back(temp1); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(16); shader_code_.push_back(16); shader_code_.push_back(16); shader_code_.push_back(16); ++stat_.instruction_count; ++stat_.uint_instruction_count; // 16-in-32: Write the high 16 bits to temp1. // bfi temp1, l(16, 16, 16, 16), l(16, 16, 16, 16), pv, temp1 // pv: ABCD/BADC, temp1: CDAB/DCBA, temp2: 8in16/8in32?|8in32/16in32? shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_BFI) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(17)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, write_mask, 1)); shader_code_.push_back(temp1); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(16); shader_code_.push_back(16); shader_code_.push_back(16); shader_code_.push_back(16); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(16); shader_code_.push_back(16); shader_code_.push_back(16); shader_code_.push_back(16); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(temp1); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Write the swapped value to pv. // movc pv, temp2.yyyy, temp1, pv shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, write_mask, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(temp2); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(temp1); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); ++stat_.instruction_count; ++stat_.movc_instruction_count; PopSystemTemp(2); } void DxbcShaderTranslator::ProcessVertexFetchInstruction( const ParsedVertexFetchInstruction& instr) { if (instr.operand_count < 2 || instr.operands[1].storage_source != InstructionStorageSource::kVertexFetchConstant) { assert_always(); return; } // Get the mask for ld_raw and byte swapping. uint32_t load_dword_count; switch (instr.attributes.data_format) { case VertexFormat::k_8_8_8_8: case VertexFormat::k_2_10_10_10: case VertexFormat::k_10_11_11: case VertexFormat::k_11_11_10: case VertexFormat::k_16_16: case VertexFormat::k_16_16_FLOAT: case VertexFormat::k_32: case VertexFormat::k_32_FLOAT: load_dword_count = 1; break; case VertexFormat::k_16_16_16_16: case VertexFormat::k_16_16_16_16_FLOAT: case VertexFormat::k_32_32: case VertexFormat::k_32_32_FLOAT: load_dword_count = 2; break; case VertexFormat::k_32_32_32_FLOAT: load_dword_count = 3; break; case VertexFormat::k_32_32_32_32: case VertexFormat::k_32_32_32_32_FLOAT: load_dword_count = 4; break; default: assert_unhandled_case(instr.attributes.data_format); return; } // Get the result write mask. uint32_t result_component_count = GetVertexFormatComponentCount(instr.attributes.data_format); if (result_component_count == 0) { assert_always(); return; } uint32_t result_write_mask = (1 << result_component_count) - 1; if (emit_source_map_) { instruction_disassembly_buffer_.Reset(); instr.Disassemble(&instruction_disassembly_buffer_); // Will be emitted by UpdateInstructionPredication. } UpdateInstructionPredication(instr.is_predicated, instr.predicate_condition, true); // Convert the index to an integer, according to // http://web.archive.org/web/20100302145413/http://msdn.microsoft.com:80/en-us/library/bb313960.aspx // (truncating rather than flooring to skip one operation because negatives // are not valid anyway, and it's safer if -tiny value becomes 0, also doing // round-to-nearest rather than round_ne so 0.5, 1.5, 2.5, 3.5 is 1, 2, 3, 4, // which is more valid as an index sequence than 0, 2, 2, 4). DxbcSourceOperand index_operand; LoadDxbcSourceOperand(instr.operands[0], index_operand); if (instr.attributes.is_index_rounded) { shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 5 + DxbcSourceOperandLength(index_operand))); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_pv_); UseDxbcSourceOperand(index_operand, kSwizzleXYZW, 0); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0x3F000000); ++stat_.instruction_count; ++stat_.float_instruction_count; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_FTOU) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_pv_); 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; } else { shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_FTOU) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH( 3 + DxbcSourceOperandLength(index_operand))); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_pv_); UseDxbcSourceOperand(index_operand, kSwizzleXYZW, 0); ++stat_.instruction_count; ++stat_.conversion_instruction_count; } UnloadDxbcSourceOperand(index_operand); // TODO(Triang3l): Index clamping maybe. uint32_t vfetch_index = instr.operands[1].storage_index; // Get the memory address (taken from the fetch constant - the low 2 bits of // it are removed because vertices and raw buffer operations are 4-aligned and // fetch type - 3 for vertices - is stored there). Vertex fetch is specified // by 2 dwords in fetch constants, but in our case they are 4-component, so // one vector of fetch constants contains two vfetches. // TODO(Triang3l): Clamp to buffer size maybe (may be difficult if the buffer // is smaller than 16). // http://xboxforums.create.msdn.com/forums/p/7537/39919.aspx#39919 if (cbuffer_index_fetch_constants_ == kCbufferIndexUnallocated) { cbuffer_index_fetch_constants_ = cbuffer_count_++; } shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, (vfetch_index & 1) * 2, 3)); shader_code_.push_back(cbuffer_index_fetch_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kFetchConstants)); shader_code_.push_back(vfetch_index >> 1); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0x1FFFFFFC); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Calculate the address of the vertex. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_UMAD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(instr.attributes.stride * 4); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(system_temp_pv_); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Add the element offset. if (instr.attributes.offset != 0) { shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(instr.attributes.offset * 4); ++stat_.instruction_count; ++stat_.int_instruction_count; } // Select whether shared memory is an SRV or a UAV (depending on whether // memexport is used in the pipeline) - check the flag. system_constants_used_ |= 1ull << kSysConst_Flags_Index; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSysConst_Flags_Comp, 3)); shader_code_.push_back(cbuffer_index_system_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants)); shader_code_.push_back(kSysConst_Flags_Vec); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(kSysFlag_SharedMemoryIsUAV); ++stat_.instruction_count; ++stat_.uint_instruction_count; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IF) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN( D3D10_SB_INSTRUCTION_TEST_NONZERO) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(system_temp_pv_); ++stat_.instruction_count; ++stat_.dynamic_flow_control_count; // Load the vertex data from the shared memory at U0. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_LD_RAW) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(8)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, (1 << load_dword_count) - 1, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D11_SB_OPERAND_TYPE_UNORDERED_ACCESS_VIEW, kSwizzleXYZW & ((1 << (load_dword_count * 2)) - 1), 2)); shader_code_.push_back(0); shader_code_.push_back(uint32_t(UAVRegister::kSharedMemory)); ++stat_.instruction_count; ++stat_.texture_load_instructions; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ELSE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; // Load the vertex data from the shared memory at T0, register t0. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_LD_RAW) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(8)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, (1 << load_dword_count) - 1, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_RESOURCE, kSwizzleXYZW & ((1 << (load_dword_count * 2)) - 1), 2)); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.texture_load_instructions; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; // Byte swap the data. SwapVertexData(vfetch_index, (1 << load_dword_count) - 1); // Get the data needed for unpacking and converting. bool extract_signed = instr.attributes.is_signed; uint32_t extract_widths[4] = {}, extract_offsets[4] = {}; uint32_t extract_swizzle = kSwizzleXXXX; float normalize_scales[4] = {}; switch (instr.attributes.data_format) { case VertexFormat::k_8_8_8_8: extract_widths[0] = extract_widths[1] = extract_widths[2] = extract_widths[3] = 8; // Assuming little endian ByteAddressBuffer Load. extract_offsets[1] = 8; extract_offsets[2] = 16; extract_offsets[3] = 24; normalize_scales[0] = normalize_scales[1] = normalize_scales[2] = normalize_scales[3] = instr.attributes.is_signed ? (1.0f / 127.0f) : (1.0f / 255.0f); break; case VertexFormat::k_2_10_10_10: extract_widths[0] = extract_widths[1] = extract_widths[2] = 10; extract_widths[3] = 2; extract_offsets[1] = 10; extract_offsets[2] = 20; extract_offsets[3] = 30; normalize_scales[0] = normalize_scales[1] = normalize_scales[2] = instr.attributes.is_signed ? (1.0f / 511.0f) : (1.0f / 1023.0f); normalize_scales[3] = instr.attributes.is_signed ? 1.0f : (1.0f / 3.0f); break; case VertexFormat::k_10_11_11: extract_widths[0] = extract_widths[1] = 11; extract_widths[2] = 10; extract_offsets[1] = 11; extract_offsets[2] = 22; normalize_scales[0] = normalize_scales[1] = instr.attributes.is_signed ? (1.0f / 1023.0f) : (1.0f / 2047.0f); normalize_scales[2] = instr.attributes.is_signed ? (1.0f / 511.0f) : (1.0f / 1023.0f); break; case VertexFormat::k_11_11_10: extract_widths[0] = 10; extract_widths[1] = extract_widths[2] = 11; extract_offsets[1] = 10; extract_offsets[2] = 21; normalize_scales[0] = instr.attributes.is_signed ? (1.0f / 511.0f) : (1.0f / 1023.0f); normalize_scales[1] = normalize_scales[2] = instr.attributes.is_signed ? (1.0f / 1023.0f) : (1.0f / 2047.0f); break; case VertexFormat::k_16_16: extract_widths[0] = extract_widths[1] = 16; extract_offsets[1] = 16; normalize_scales[0] = normalize_scales[1] = instr.attributes.is_signed ? (1.0f / 32767.0f) : (1.0f / 65535.0f); break; case VertexFormat::k_16_16_16_16: extract_widths[0] = extract_widths[1] = extract_widths[2] = extract_widths[3] = 16; extract_offsets[1] = extract_offsets[3] = 16; extract_swizzle = 0b01010000; normalize_scales[0] = normalize_scales[1] = normalize_scales[2] = normalize_scales[3] = instr.attributes.is_signed ? (1.0f / 32767.0f) : (1.0f / 65535.0f); break; case VertexFormat::k_16_16_FLOAT: extract_signed = false; extract_widths[0] = extract_widths[1] = 16; extract_offsets[1] = 16; break; case VertexFormat::k_16_16_16_16_FLOAT: extract_signed = false; extract_widths[0] = extract_widths[1] = extract_widths[2] = extract_widths[3] = 16; extract_offsets[1] = extract_offsets[3] = 16; extract_swizzle = 0b01010000; break; // For 32-bit, extraction is not done at all, so its parameters are ignored. case VertexFormat::k_32: case VertexFormat::k_32_32: case VertexFormat::k_32_32_32_32: normalize_scales[0] = normalize_scales[1] = normalize_scales[2] = normalize_scales[3] = instr.attributes.is_signed ? (1.0f / 2147483647.0f) : (1.0f / 4294967295.0f); break; default: // 32-bit float. break; } // Extract components from packed data if needed. if (extract_widths[0] != 0) { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(extract_signed ? D3D11_SB_OPCODE_IBFE : D3D11_SB_OPCODE_UBFE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(15)); shader_code_.push_back(EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, result_write_mask, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(extract_widths[0]); shader_code_.push_back(extract_widths[1]); shader_code_.push_back(extract_widths[2]); shader_code_.push_back(extract_widths[3]); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(extract_offsets[0]); shader_code_.push_back(extract_offsets[1]); shader_code_.push_back(extract_offsets[2]); shader_code_.push_back(extract_offsets[3]); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, extract_swizzle, 1)); shader_code_.push_back(system_temp_pv_); ++stat_.instruction_count; if (extract_signed) { ++stat_.int_instruction_count; } else { ++stat_.uint_instruction_count; } } // Convert to float and normalize if needed. if (instr.attributes.data_format == VertexFormat::k_16_16_FLOAT || instr.attributes.data_format == VertexFormat::k_16_16_16_16_FLOAT) { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_F16TOF32) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back(EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, result_write_mask, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); ++stat_.instruction_count; ++stat_.conversion_instruction_count; } else if (normalize_scales[0] != 0.0f) { // If no normalize_scales, it's a float value already. Otherwise, convert to // float and normalize if needed. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(instr.attributes.is_signed ? D3D10_SB_OPCODE_ITOF : D3D10_SB_OPCODE_UTOF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back(EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, result_write_mask, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); ++stat_.instruction_count; ++stat_.conversion_instruction_count; if (!instr.attributes.is_integer) { // Normalize. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MUL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, result_write_mask, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); for (uint32_t i = 0; i < 4; ++i) { shader_code_.push_back( reinterpret_cast(normalize_scales)[i]); } ++stat_.instruction_count; ++stat_.float_instruction_count; // Clamp to -1 (both -127 and -128 should be -1 in graphics APIs for // snorm8). if (instr.attributes.is_signed) { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAX) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, result_write_mask, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0xBF800000u); shader_code_.push_back(0xBF800000u); shader_code_.push_back(0xBF800000u); shader_code_.push_back(0xBF800000u); ++stat_.instruction_count; ++stat_.float_instruction_count; } } } // Zero unused components if loaded a 32-bit component (because it's not // bfe'd, in this case, the unused components would have been zeroed already). if (extract_widths[0] == 0 && result_write_mask != 0b1111) { shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(8)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, 0b1111 & ~result_write_mask, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.mov_instruction_count; } // Apply the exponent bias. if (instr.attributes.exp_adjust != 0) { shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MUL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back(EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, result_write_mask, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); uint32_t exp_adjust_scale = uint32_t(0x3F800000 + (instr.attributes.exp_adjust << 23)); shader_code_.push_back(exp_adjust_scale); shader_code_.push_back(exp_adjust_scale); shader_code_.push_back(exp_adjust_scale); shader_code_.push_back(exp_adjust_scale); ++stat_.instruction_count; ++stat_.float_instruction_count; } StoreResult(instr.result, system_temp_pv_, false); } uint32_t DxbcShaderTranslator::FindOrAddTextureSRV(uint32_t fetch_constant, TextureDimension dimension, bool is_signed, bool is_sign_required) { // 1D and 2D textures (including stacked ones) are treated as 2D arrays for // binding and coordinate simplicity. if (dimension == TextureDimension::k1D) { dimension = TextureDimension::k2D; } // 1 is added to the return value because T0/t0 is shared memory. for (uint32_t i = 0; i < uint32_t(texture_srvs_.size()); ++i) { TextureSRV& texture_srv = texture_srvs_[i]; if (texture_srv.fetch_constant == fetch_constant && texture_srv.dimension == dimension && texture_srv.is_signed == is_signed) { if (is_sign_required && !texture_srv.is_sign_required) { // kGetTextureComputedLod uses only the unsigned SRV, which means it // must be bound even when all components are signed. texture_srv.is_sign_required = true; } return 1 + i; } } if (texture_srvs_.size() >= kMaxTextureSRVs) { assert_always(); return 1 + (kMaxTextureSRVs - 1); } TextureSRV new_texture_srv; new_texture_srv.fetch_constant = fetch_constant; new_texture_srv.dimension = dimension; new_texture_srv.is_signed = is_signed; new_texture_srv.is_sign_required = is_sign_required; const char* dimension_name; switch (dimension) { case TextureDimension::k3D: dimension_name = "3d"; break; case TextureDimension::kCube: dimension_name = "cube"; break; default: dimension_name = "2d"; } new_texture_srv.name = xe::format_string("xe_texture%u_%s_%c", fetch_constant, dimension_name, is_signed ? 's' : 'u'); uint32_t srv_register = 1 + uint32_t(texture_srvs_.size()); texture_srvs_.emplace_back(std::move(new_texture_srv)); return srv_register; } uint32_t DxbcShaderTranslator::FindOrAddSamplerBinding( uint32_t fetch_constant, TextureFilter mag_filter, TextureFilter min_filter, TextureFilter mip_filter, AnisoFilter aniso_filter) { // In Direct3D 12, anisotropic filtering implies linear filtering. if (aniso_filter != AnisoFilter::kDisabled && aniso_filter != AnisoFilter::kUseFetchConst) { mag_filter = TextureFilter::kLinear; min_filter = TextureFilter::kLinear; mip_filter = TextureFilter::kLinear; aniso_filter = std::min(aniso_filter, AnisoFilter::kMax_16_1); } for (uint32_t i = 0; i < uint32_t(sampler_bindings_.size()); ++i) { const SamplerBinding& sampler_binding = sampler_bindings_[i]; if (sampler_binding.fetch_constant == fetch_constant && sampler_binding.mag_filter == mag_filter && sampler_binding.min_filter == min_filter && sampler_binding.mip_filter == mip_filter && sampler_binding.aniso_filter == aniso_filter) { return i; } } if (sampler_bindings_.size() >= kMaxSamplerBindings) { assert_always(); return kMaxSamplerBindings - 1; } std::ostringstream name; name << "xe_sampler" << fetch_constant; if (aniso_filter != AnisoFilter::kUseFetchConst) { if (aniso_filter == AnisoFilter::kDisabled) { name << "_a0"; } else { name << "_a" << (1u << (uint32_t(aniso_filter) - 1)); } } if (aniso_filter == AnisoFilter::kDisabled || aniso_filter == AnisoFilter::kUseFetchConst) { static const char* kFilterSuffixes[] = {"p", "l", "b", "f"}; name << "_" << kFilterSuffixes[uint32_t(mag_filter)] << kFilterSuffixes[uint32_t(min_filter)] << kFilterSuffixes[uint32_t(mip_filter)]; } SamplerBinding new_sampler_binding; new_sampler_binding.fetch_constant = fetch_constant; new_sampler_binding.mag_filter = mag_filter; new_sampler_binding.min_filter = min_filter; new_sampler_binding.mip_filter = mip_filter; new_sampler_binding.aniso_filter = aniso_filter; new_sampler_binding.name = name.str(); uint32_t sampler_register = uint32_t(sampler_bindings_.size()); sampler_bindings_.emplace_back(std::move(new_sampler_binding)); return sampler_register; } void DxbcShaderTranslator::ArrayCoordToCubeDirection(uint32_t reg) { // This does the reverse of what the cube vector ALU instruction does, but // assuming S and T are normalized. // // The major axis depends on the face index (passed as a float in reg.z): // +X for 0, -X for 1, +Y for 2, -Y for 3, +Z for 4, -Z for 5. // // If the major axis is X: // * X is 1.0 or -1.0. // * Y is -T. // * Z is -S for positive X, +S for negative X. // If it's Y: // * X is +S. // * Y is 1.0 or -1.0. // * Z is +T for positive Y, -T for negative Y. // If it's Z: // * X is +S for positive Z, -S for negative Z. // * Y is -T. // * Z is 1.0 or -1.0. // Make 0, not 0.5, the center of S and T. // mad reg.xy__, reg.xy__, l(2.0, 2.0, _, _), l(-1.0, -1.0, _, _) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(15)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0011, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(reg); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0x40000000u); shader_code_.push_back(0x40000000u); shader_code_.push_back(0x3F800000u); shader_code_.push_back(0x3F800000u); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0xBF800000u); shader_code_.push_back(0xBF800000u); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.float_instruction_count; // Clamp the face index to 0...5 for safety (in case an offset was applied). // max reg.z, reg.z, l(0.0) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAX) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.float_instruction_count; // min reg.z, reg.z, l(5.0) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MIN) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0x40A00000); ++stat_.instruction_count; ++stat_.float_instruction_count; // Allocate a register for major axis info. uint32_t major_axis_temp = PushSystemTemp(); // Convert the face index to an integer. // ftou major_axis_temp.x, reg.z shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_FTOU) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(major_axis_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(reg); ++stat_.instruction_count; ++stat_.conversion_instruction_count; // Split the face number into major axis number and direction. // ubfe major_axis_temp.x__w, l(2, _, _, 1), l(1, _, _, 0), // major_axis_temp.x__x shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_UBFE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(15)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1001, 1)); shader_code_.push_back(major_axis_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(2); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(1); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(1); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(major_axis_temp); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Make booleans for whether each axis is major. // ieq major_axis_temp.xyz_, major_axis_temp.xxx_, l(0, 1, 2, _) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IEQ) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(major_axis_temp); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(major_axis_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(1); shader_code_.push_back(2); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.int_instruction_count; // Replace the face index in the source/destination with 1.0 or -1.0 for // swizzling. // movc reg.z, major_axis_temp.w, l(-1.0), l(1.0) shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(major_axis_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0xBF800000u); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0x3F800000u); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Swizzle and negate the coordinates depending on which axis is major, but // don't negate according to the direction of the major axis (will be done // later). // X case. // movc reg.xyz_, major_axis_temp.xxx_, reg.zyx_, reg.xyz_ shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(major_axis_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b11000110, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(reg); ++stat_.instruction_count; ++stat_.movc_instruction_count; // movc reg._yz_, major_axis_temp._xx_, -reg._yz_, reg._yz_ shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0110, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(major_axis_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1) | ENCODE_D3D10_SB_OPERAND_EXTENDED(1)); shader_code_.push_back( ENCODE_D3D10_SB_EXTENDED_OPERAND_MODIFIER(D3D10_SB_OPERAND_MODIFIER_NEG)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(reg); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Y case. // movc reg._yz_, major_axis_temp._yy_, reg._zy_, reg._yz_ shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0110, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(major_axis_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b11011000, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(reg); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Z case. // movc reg.y, major_axis_temp.z, -reg.y, reg.y shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(major_axis_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1) | ENCODE_D3D10_SB_OPERAND_EXTENDED(1)); shader_code_.push_back( ENCODE_D3D10_SB_EXTENDED_OPERAND_MODIFIER(D3D10_SB_OPERAND_MODIFIER_NEG)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(reg); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Flip coordinates according to the direction of the major axis. // Z needs to be flipped if the major axis is X or Y, so make an X || Y mask. // X is flipped only when the major axis is Z. // or major_axis_temp.x, major_axis_temp.x, major_axis_temp.y shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_OR) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(major_axis_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(major_axis_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(major_axis_temp); ++stat_.instruction_count; ++stat_.uint_instruction_count; // If the major axis is positive, nothing needs to be flipped. We have // 0xFFFFFFFF/0 at this point in the major axis mask, but 1/0 in the major // axis direction (didn't include W in ieq to waste less scalar operations), // but AND would result in 1/0, which is fine for movc too. // and major_axis_temp.x_z_, major_axis_temp.x_z_, major_axis_temp.w_w_ shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0101, 1)); shader_code_.push_back(major_axis_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(major_axis_temp); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(major_axis_temp); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Flip axes that need to be flipped. // movc reg.x_z_, major_axis_temp.z_x_, -reg.x_z_, reg.x_z_ shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0101, 1)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b11000110, 1)); shader_code_.push_back(major_axis_temp); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1) | ENCODE_D3D10_SB_OPERAND_EXTENDED(1)); shader_code_.push_back( ENCODE_D3D10_SB_EXTENDED_OPERAND_MODIFIER(D3D10_SB_OPERAND_MODIFIER_NEG)); shader_code_.push_back(reg); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(reg); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Release major_axis_temp. PopSystemTemp(); } void DxbcShaderTranslator::ProcessTextureFetchInstruction( const ParsedTextureFetchInstruction& instr) { if (emit_source_map_) { instruction_disassembly_buffer_.Reset(); instr.Disassemble(&instruction_disassembly_buffer_); // Will be emitted later explicitly or by UpdateInstructionPredication. } // Predication should not affect derivative calculation: // https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/dx9-graphics-reference-asm-ps-registers-output-color // Do the part involving derivative calculation unconditionally, and re-enter // the predicate check before writing the result. bool suppress_predication = false; if (IsDxbcPixelShader()) { if (instr.opcode == FetchOpcode::kGetTextureComputedLod || instr.opcode == FetchOpcode::kGetTextureGradients) { suppress_predication = true; } else if (instr.opcode == FetchOpcode::kTextureFetch) { suppress_predication = instr.attributes.use_computed_lod && !instr.attributes.use_register_lod; } } uint32_t exec_p0_temp = UINT32_MAX; if (suppress_predication) { // Emit the disassembly before all this to indicate the reason of going // unconditional. EmitInstructionDisassembly(); // Close instruction-level predication. CloseInstructionPredication(); // Temporarily close exec-level predication - will reopen at the end, so not // changing cf_exec_predicated_. if (cf_exec_predicated_) { if (cf_exec_predicate_written_) { // Restore the predicate value in the beginning of the exec and put it // in exec_p0_temp. exec_p0_temp = PushSystemTemp(); // `if` case - the value was cf_exec_predicate_condition_. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(exec_p0_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(cf_exec_predicate_condition_ ? 0xFFFFFFFFu : 0u); ++stat_.instruction_count; ++stat_.mov_instruction_count; shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ELSE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; // `else` case - the value was !cf_exec_predicate_condition_. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(exec_p0_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(cf_exec_predicate_condition_ ? 0u : 0xFFFFFFFFu); ++stat_.instruction_count; ++stat_.mov_instruction_count; } shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; } } else { UpdateInstructionPredication(instr.is_predicated, instr.predicate_condition, true); } bool store_result = false; // Whether the result is only in X and all components should be remapped to X // while storing. bool replicate_result = false; DxbcSourceOperand operand; uint32_t operand_length = 0; if (instr.operand_count >= 1) { LoadDxbcSourceOperand(instr.operands[0], operand); operand_length = DxbcSourceOperandLength(operand); } uint32_t tfetch_index = instr.operands[1].storage_index; // Fetch constants are laid out like: // tf0[0] tf0[1] tf0[2] tf0[3] // tf0[4] tf0[5] tf1[0] tf1[1] // tf1[2] tf1[3] tf1[4] tf1[5] uint32_t tfetch_pair_offset = (tfetch_index >> 1) * 3; // TODO(Triang3l): kGetTextureBorderColorFrac. if (!IsDxbcPixelShader() && (instr.opcode == FetchOpcode::kGetTextureComputedLod || instr.opcode == FetchOpcode::kGetTextureGradients)) { // Quickly skip everything if tried to get anything involving derivatives // not in a pixel shader because only the pixel shader has derivatives. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(8)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.mov_instruction_count; } else if (instr.opcode == FetchOpcode::kTextureFetch || instr.opcode == FetchOpcode::kGetTextureComputedLod || instr.opcode == FetchOpcode::kGetTextureWeights) { store_result = true; // 0 is unsigned, 1 is signed. uint32_t srv_registers[2] = {UINT32_MAX, UINT32_MAX}; uint32_t srv_registers_stacked[2] = {UINT32_MAX, UINT32_MAX}; uint32_t sampler_register = UINT32_MAX; // Only the fetch constant needed for kGetTextureWeights. if (instr.opcode != FetchOpcode::kGetTextureWeights) { if (instr.opcode == FetchOpcode::kGetTextureComputedLod) { // The LOD is a scalar and it doesn't depend on the texture contents, so // require any variant - unsigned in this case because more texture // formats support it. srv_registers[0] = FindOrAddTextureSRV(tfetch_index, instr.dimension, false, true); if (instr.dimension == TextureDimension::k3D) { // 3D or 2D stacked is selected dynamically. srv_registers_stacked[0] = FindOrAddTextureSRV( tfetch_index, TextureDimension::k2D, false, true); } } else { srv_registers[0] = FindOrAddTextureSRV(tfetch_index, instr.dimension, false); srv_registers[1] = FindOrAddTextureSRV(tfetch_index, instr.dimension, true); if (instr.dimension == TextureDimension::k3D) { // 3D or 2D stacked is selected dynamically. srv_registers_stacked[0] = FindOrAddTextureSRV(tfetch_index, TextureDimension::k2D, false); srv_registers_stacked[1] = FindOrAddTextureSRV(tfetch_index, TextureDimension::k2D, true); } } sampler_register = FindOrAddSamplerBinding( tfetch_index, instr.attributes.mag_filter, instr.attributes.min_filter, instr.attributes.mip_filter, instr.attributes.aniso_filter); } uint32_t coord_temp = PushSystemTemp(); // Move coordinates to pv temporarily so zeros can be added to expand them // to Texture2DArray coordinates and to apply offset. Or, if the instruction // is getWeights, move them to pv because their fractional part will be // returned. uint32_t coord_mask = 0b0111; switch (instr.dimension) { case TextureDimension::k1D: coord_mask = 0b0001; break; case TextureDimension::k2D: coord_mask = 0b0011; break; case TextureDimension::k3D: coord_mask = 0b0111; break; case TextureDimension::kCube: // Don't need the 3rd component for getWeights because it's the face // index, so it doesn't participate in bilinear filtering. coord_mask = instr.opcode == FetchOpcode::kGetTextureWeights ? 0b0011 : 0b0111; break; } shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3 + operand_length)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, coord_mask, 1)); shader_code_.push_back(coord_temp); UseDxbcSourceOperand(operand); ++stat_.instruction_count; ++stat_.mov_instruction_count; // If 1D or 2D, fill the unused coordinates with zeros (sampling the only // row of the only slice). For getWeights, also clear the 4th component // because the coordinates will be returned. uint32_t coord_all_components_mask = instr.opcode == FetchOpcode::kGetTextureWeights ? 0b1111 : 0b0111; uint32_t coord_zero_mask = coord_all_components_mask & ~coord_mask; if (coord_zero_mask) { shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(8)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, coord_zero_mask, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.mov_instruction_count; } // Get the offset to see if the size of the texture is needed. // It's probably applicable to tfetchCube too, we're going to assume it's // used for them the same way as for stacked textures. // http://web.archive.org/web/20090511231340/http://msdn.microsoft.com:80/en-us/library/bb313959.aspx // Adding 1/1024 - quarter of one fixed-point unit of subpixel precision // (not to touch rounding when the GPU is converting to fixed-point) - to // resolve the ambiguity when the texture coordinate is directly between two // pixels, which hurts nearest-neighbor sampling (fixes the XBLA logo being // blocky in Banjo-Kazooie and the outlines around things and overall // blockiness in Halo 3). // TODO(Triang3l): Investigate the sign of this epsilon, because in Halo 3 // positive causes thin outlines with MSAA (with ROV), and negative causes // thick outlines with SSAA there. float offset_x = instr.attributes.offset_x + (1.0f / 1024.0f); if (instr.opcode == FetchOpcode::kGetTextureWeights) { // Bilinear filtering (for shadows, for instance, in Halo 3), 0.5 - // exactly the pixel. offset_x -= 0.5f; } float offset_y = 0.0f, offset_z = 0.0f; if (instr.dimension == TextureDimension::k2D || instr.dimension == TextureDimension::k3D || instr.dimension == TextureDimension::kCube) { offset_y = instr.attributes.offset_y + (1.0f / 1024.0f); if (instr.opcode == FetchOpcode::kGetTextureWeights) { offset_y -= 0.5f; } // Don't care about the Z offset for cubemaps when getting weights because // zero Z will be returned anyway (the face index doesn't participate in // bilinear filtering). if (instr.dimension == TextureDimension::k3D || (instr.dimension == TextureDimension::kCube && instr.opcode != FetchOpcode::kGetTextureWeights)) { offset_z = instr.attributes.offset_z; if (instr.dimension == TextureDimension::k3D) { // Z is the face index for cubemaps, so don't apply the epsilon to it. offset_z += 1.0f / 1024.0f; if (instr.opcode == FetchOpcode::kGetTextureWeights) { offset_z -= 0.5f; } } } } // Gather info about filtering across array layers. // Use the magnification filter when no derivatives: // https://stackoverflow.com/questions/40328956/difference-between-sample-and-samplelevel-wrt-texture-filtering bool vol_min_filter_applicable = instr.opcode == FetchOpcode::kTextureFetch && (instr.attributes.use_register_gradients || (instr.attributes.use_computed_lod && IsDxbcPixelShader())); bool has_vol_mag_filter = instr.attributes.vol_mag_filter != TextureFilter::kUseFetchConst; bool has_vol_min_filter = vol_min_filter_applicable ? instr.attributes.vol_min_filter != TextureFilter::kUseFetchConst : has_vol_mag_filter; bool vol_mag_filter_linear = instr.attributes.vol_mag_filter == TextureFilter::kLinear; bool vol_min_filter_linear = vol_min_filter_applicable ? instr.attributes.vol_min_filter == TextureFilter::kLinear : vol_mag_filter_linear; bool vol_mag_filter_point = has_vol_mag_filter && !vol_mag_filter_linear; bool vol_min_filter_point = has_vol_min_filter && !vol_min_filter_linear; // Get the texture size if needed, apply offset and switch between // normalized and unnormalized coordinates if needed. The offset is // fractional on the Xbox 360 (has 0.5 granularity), unlike in Direct3D 12, // and cubemaps possibly can have offset and their coordinates are different // than in Direct3D 12 (like an array texture rather than a direction). // getWeights instructions also need the texture size because they work like // frac(coord * texture_size). // TODO(Triang3l): Unnormalized coordinates should be disabled when the // wrap mode is not a clamped one, though it's probably a very rare case, // unlikely to be used on purpose. // http://web.archive.org/web/20090514012026/http://msdn.microsoft.com:80/en-us/library/bb313957.aspx uint32_t size_and_is_3d_temp = UINT32_MAX; // For stacked textures, if point sampling is not forced in the instruction: // X - whether linear filtering should be done across layers (for color // grading LUTs in Unreal Engine 3 games and Burnout Revenge), unless // the filter is known from the instruction for all cases. // Y - lerp factor between the two layers, unless only point sampling can be // used. uint32_t vol_filter_temp = UINT32_MAX; bool vol_filter_temp_linear_test = D3D10_SB_INSTRUCTION_TEST_NONZERO; // With 1/1024 this will always be true anyway, but let's keep the shorter // path without the offset in case some day this hack won't be used anymore // somehow. bool has_offset = offset_x != 0.0f || offset_y != 0.0f || offset_z != 0.0f; if (instr.opcode == FetchOpcode::kGetTextureWeights || has_offset || instr.attributes.unnormalized_coordinates || instr.dimension == TextureDimension::k3D) { size_and_is_3d_temp = PushSystemTemp(); if (instr.opcode == FetchOpcode::kTextureFetch && instr.dimension == TextureDimension::k3D) { uint32_t vol_filter_temp_components = 0b0000; if (!has_vol_mag_filter || !has_vol_min_filter || vol_mag_filter_linear != vol_min_filter_linear) { vol_filter_temp_components |= 0b0011; } else if (vol_mag_filter_linear || vol_min_filter_linear) { vol_filter_temp_components |= 0b0010; } // Initialize to 0 to break register dependency. if (vol_filter_temp_components != 0) { vol_filter_temp = PushSystemTemp(vol_filter_temp_components); } } // Will use fetch constants for the size and for stacked texture filter. if (cbuffer_index_fetch_constants_ == kCbufferIndexUnallocated) { cbuffer_index_fetch_constants_ = cbuffer_count_++; } // Get 2D texture size and array layer count, in bits 0:12, 13:25, 26:31 // of dword 2 ([0].z or [2].x). shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_UBFE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(17)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, coord_mask, 1)); shader_code_.push_back(size_and_is_3d_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(13); shader_code_.push_back(instr.dimension != TextureDimension::k1D ? 13 : 0); shader_code_.push_back(instr.dimension == TextureDimension::k3D ? 6 : 0); shader_code_.push_back(0); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(13); shader_code_.push_back(26); shader_code_.push_back(0); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, 2 - 2 * (tfetch_index & 1), 3)); shader_code_.push_back(cbuffer_index_fetch_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kFetchConstants)); shader_code_.push_back(tfetch_pair_offset + (tfetch_index & 1) * 2); ++stat_.instruction_count; ++stat_.uint_instruction_count; if (instr.dimension == TextureDimension::k3D) { // Write whether the texture is 3D to W if it's 3D/stacked, as // 0xFFFFFFFF for 3D or 0 for stacked. The dimension is in dword 5 in // bits 9:10. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1000, 1)); shader_code_.push_back(size_and_is_3d_temp); // Dword 5 is [1].y or [2].w. shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, 1 + 2 * (tfetch_index & 1), 3)); shader_code_.push_back(cbuffer_index_fetch_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kFetchConstants)); shader_code_.push_back(tfetch_pair_offset + 1 + (tfetch_index & 1)); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0x3 << 9); ++stat_.instruction_count; ++stat_.uint_instruction_count; shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IEQ) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1000, 1)); shader_code_.push_back(size_and_is_3d_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(size_and_is_3d_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(uint32_t(Dimension::k3D) << 9); ++stat_.instruction_count; ++stat_.int_instruction_count; // Check if need to replace the size with 3D texture size. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IF) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN( D3D10_SB_INSTRUCTION_TEST_NONZERO) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(size_and_is_3d_temp); ++stat_.instruction_count; ++stat_.dynamic_flow_control_count; // Extract 3D texture size (in the same constant, but 11:11:10). shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_UBFE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(17)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(size_and_is_3d_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(11); shader_code_.push_back(11); shader_code_.push_back(10); shader_code_.push_back(0); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0); shader_code_.push_back(11); shader_code_.push_back(22); shader_code_.push_back(0); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, 2 - 2 * (tfetch_index & 1), 3)); shader_code_.push_back(cbuffer_index_fetch_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kFetchConstants)); shader_code_.push_back(tfetch_pair_offset + (tfetch_index & 1) * 2); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Done replacing. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; } // Convert the size to float. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_UTOF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, coord_mask, 1)); shader_code_.push_back(size_and_is_3d_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(size_and_is_3d_temp); ++stat_.instruction_count; ++stat_.conversion_instruction_count; // Add 1 to the size because fetch constants store size minus one. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, coord_mask, 1)); shader_code_.push_back(size_and_is_3d_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(size_and_is_3d_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0x3F800000); shader_code_.push_back(0x3F800000); shader_code_.push_back(0x3F800000); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.float_instruction_count; if (instr.opcode == FetchOpcode::kGetTextureWeights) { // Weights for bilinear filtering - need to get the fractional part of // unnormalized coordinates. if (instr.attributes.unnormalized_coordinates) { if (has_offset) { // Apply the offset. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, coord_mask, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back( *reinterpret_cast(&offset_x)); shader_code_.push_back( *reinterpret_cast(&offset_y)); shader_code_.push_back( *reinterpret_cast(&offset_z)); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.float_instruction_count; } } else { // Unnormalize the coordinates and apply the offset. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(has_offset ? D3D10_SB_OPCODE_MAD : D3D10_SB_OPCODE_MUL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(has_offset ? 12 : 7)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, coord_mask, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(size_and_is_3d_temp); if (has_offset) { shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back( *reinterpret_cast(&offset_x)); shader_code_.push_back( *reinterpret_cast(&offset_y)); shader_code_.push_back( *reinterpret_cast(&offset_z)); shader_code_.push_back(0); } ++stat_.instruction_count; ++stat_.float_instruction_count; } } else { // Texture fetch - need to get normalized coordinates (with unnormalized // Z for stacked textures). if (has_offset || instr.attributes.unnormalized_coordinates) { // Take the reciprocal of the size to normalize the UV coordinates and // the offset. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_RCP) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, coord_mask & 0b0011, 1)); shader_code_.push_back(size_and_is_3d_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(size_and_is_3d_temp); ++stat_.instruction_count; ++stat_.float_instruction_count; // Normalize the coordinates. if (instr.attributes.unnormalized_coordinates) { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MUL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, coord_mask & 0b0011, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(size_and_is_3d_temp); ++stat_.instruction_count; ++stat_.float_instruction_count; } // Apply the offset (coord = offset * 1/size + coord). if (has_offset) { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(12)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, coord_mask & 0b0011, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back( *reinterpret_cast(&offset_x)); shader_code_.push_back( *reinterpret_cast(&offset_y)); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(size_and_is_3d_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(coord_temp); ++stat_.instruction_count; ++stat_.float_instruction_count; } } if (instr.dimension == TextureDimension::k3D) { // Both 3D textures and 2D arrays have their Z coordinate normalized, // however, on PC, array elements have unnormalized indices. // https://www.slideshare.net/blackdevilvikas/next-generation-graphics-programming-on-xbox-360 // The offset must be handled not only for 3D textures, but for the // array layer too - used in Halo 3. // Check if stacked. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IF) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN( D3D10_SB_INSTRUCTION_TEST_ZERO) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(size_and_is_3d_temp); ++stat_.instruction_count; ++stat_.dynamic_flow_control_count; // Layers on the Xenos are indexed like texels, with 0.5 being exactly // layer 0, but in D3D10+ 0.0 is exactly layer 0. Halo 3 uses i + 0.5 // offset for lightmap index, for instance. float offset_layer = offset_z - 0.5f; if (instr.attributes.unnormalized_coordinates) { if (offset_layer != 0.0f) { // Add the offset to the array layer. 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, 0b0100, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back( *reinterpret_cast(&offset_layer)); ++stat_.instruction_count; ++stat_.float_instruction_count; } } else { // Unnormalize the array layer and apply the offset. if (offset_layer != 0.0f) { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); } else { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MUL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); } shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(size_and_is_3d_temp); if (offset_layer != 0.0f) { shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back( *reinterpret_cast(&offset_layer)); } ++stat_.instruction_count; ++stat_.float_instruction_count; } if (vol_filter_temp != UINT32_MAX) { if (vol_min_filter_applicable) { if (!has_vol_mag_filter || !has_vol_min_filter || vol_mag_filter_linear != vol_min_filter_linear) { // Check if magnifying (derivative <= 1, according to OpenGL // rules) or minifying (> 1) the texture across Z. Get the // maximum of absolutes of the two derivatives of the array // layer, either explicit or implicit. if (instr.attributes.use_register_gradients) { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAX) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(vol_filter_temp); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_TEMP, 2, 1) | ENCODE_D3D10_SB_OPERAND_EXTENDED(1)); shader_code_.push_back( ENCODE_D3D10_SB_EXTENDED_OPERAND_MODIFIER( D3D10_SB_OPERAND_MODIFIER_ABS)); shader_code_.push_back(system_temp_grad_h_lod_); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_TEMP, 2, 1) | ENCODE_D3D10_SB_OPERAND_EXTENDED(1)); shader_code_.push_back( ENCODE_D3D10_SB_EXTENDED_OPERAND_MODIFIER( D3D10_SB_OPERAND_MODIFIER_ABS)); shader_code_.push_back(system_temp_grad_v_); ++stat_.instruction_count; ++stat_.float_instruction_count; } else { for (uint32_t i = 0; i < 2; ++i) { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE( i ? D3D11_SB_OPCODE_DERIV_RTY_COARSE : D3D11_SB_OPCODE_DERIV_RTX_COARSE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, 1 << i, 1)); shader_code_.push_back(vol_filter_temp); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(coord_temp); ++stat_.instruction_count; ++stat_.float_instruction_count; } shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAX) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(vol_filter_temp); 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(vol_filter_temp); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_TEMP, 1, 1) | ENCODE_D3D10_SB_OPERAND_EXTENDED(1)); shader_code_.push_back( ENCODE_D3D10_SB_EXTENDED_OPERAND_MODIFIER( D3D10_SB_OPERAND_MODIFIER_ABS)); shader_code_.push_back(vol_filter_temp); ++stat_.instruction_count; ++stat_.float_instruction_count; } // Check if minifying. 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, 0b0001, 1)); shader_code_.push_back(vol_filter_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0x3F800000); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(vol_filter_temp); ++stat_.instruction_count; ++stat_.float_instruction_count; if (has_vol_mag_filter || has_vol_min_filter) { if (has_vol_mag_filter && has_vol_min_filter) { // Both from the instruction. assert_true(vol_mag_filter_linear != vol_min_filter_linear); if (vol_mag_filter_linear) { // Either linear when minifying (non-zero) or linear when // magnifying (zero). vol_filter_temp_linear_test = D3D10_SB_INSTRUCTION_TEST_ZERO; } } else { // Check if need to use the filter from the fetch constant. // Has mag filter - need the fetch constant filter when // minifying (non-zero minification test result). // Has min filter - need it when magnifying (zero). shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IF) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN( has_vol_mag_filter ? D3D10_SB_INSTRUCTION_TEST_NONZERO : D3D10_SB_INSTRUCTION_TEST_ZERO) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(vol_filter_temp); ++stat_.instruction_count; ++stat_.dynamic_flow_control_count; // Take the filter from the dword 4 of the fetch constant // ([1].x or [2].z) if it's not in the instruction. // Has mag filter - this will be executed for minification // (bit 1). // Has min filter - for magnification (bit 0). shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(vol_filter_temp); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, 2 * (tfetch_index & 1), 3)); shader_code_.push_back(cbuffer_index_fetch_constants_); shader_code_.push_back( uint32_t(CbufferRegister::kFetchConstants)); shader_code_.push_back(tfetch_pair_offset + 1 + (tfetch_index & 1)); shader_code_.push_back(EncodeScalarOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(has_vol_mag_filter ? (1 << 1) : (1 << 0)); ++stat_.instruction_count; ++stat_.uint_instruction_count; // If not using the filter from the fetch constant, set the // value from the instruction. // Need to change this for: // - Magnifying (zero set) and linear (non-zero needed) vol // mag filter. // - Minifying (non-zero set) and point (zero needed) vol // min filter. // Already the expected zero or non-zero value for: // - Magnifying (zero set) and point (zero needed) vol mag // filter. // - Minifying (non-zero set) and linear (non-zero needed) // vol min filter. if (vol_mag_filter_linear || vol_min_filter_point) { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ELSE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(vol_filter_temp); shader_code_.push_back(EncodeScalarOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(uint32_t(has_vol_mag_filter)); ++stat_.instruction_count; ++stat_.mov_instruction_count; } // Close the fetch constant filter check. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; } } else { // Mask the bit offset (1 for vol_min_filter, 0 for // vol_mag_filter) in the fetch constant. 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(vol_filter_temp); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(vol_filter_temp); shader_code_.push_back(EncodeScalarOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(1); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Extract the filter from dword 4 of the fetch constant // ([1].x or [2].z). shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_UBFE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(11)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(vol_filter_temp); shader_code_.push_back(EncodeScalarOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(1); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(vol_filter_temp); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, 2 * (tfetch_index & 1), 3)); shader_code_.push_back(cbuffer_index_fetch_constants_); shader_code_.push_back( uint32_t(CbufferRegister::kFetchConstants)); shader_code_.push_back(tfetch_pair_offset + 1 + (tfetch_index & 1)); ++stat_.instruction_count; ++stat_.uint_instruction_count; } } } else { if (!has_vol_mag_filter) { // Extract the magnification filter when there are no // derivatives from bit 0 of dword 4 of the fetch constant // ([1].x or [2].z). shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(vol_filter_temp); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, 2 * (tfetch_index & 1), 3)); shader_code_.push_back(cbuffer_index_fetch_constants_); shader_code_.push_back( uint32_t(CbufferRegister::kFetchConstants)); shader_code_.push_back(tfetch_pair_offset + 1 + (tfetch_index & 1)); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(1); ++stat_.instruction_count; ++stat_.uint_instruction_count; } } } if (!vol_mag_filter_point || !vol_min_filter_point) { if (!vol_mag_filter_linear || !vol_min_filter_linear) { // Check if using linear filtering between array layers. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IF) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN( vol_filter_temp_linear_test) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(vol_filter_temp); ++stat_.instruction_count; ++stat_.dynamic_flow_control_count; } // Floor the layer index to get the linear interpolation factor. 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, 0b0010, 1)); shader_code_.push_back(vol_filter_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(coord_temp); ++stat_.instruction_count; ++stat_.float_instruction_count; // Get the fraction of the layer index, with i + 0.5 right between // layers, as the linear interpolation factor between layers Z and // Z + 1. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(8)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1)); shader_code_.push_back(vol_filter_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1) | ENCODE_D3D10_SB_OPERAND_EXTENDED(1)); shader_code_.push_back(ENCODE_D3D10_SB_EXTENDED_OPERAND_MODIFIER( D3D10_SB_OPERAND_MODIFIER_NEG)); shader_code_.push_back(vol_filter_temp); ++stat_.instruction_count; ++stat_.float_instruction_count; // Floor the layer index again for sampling. 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, 0b0100, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(coord_temp); ++stat_.instruction_count; ++stat_.float_instruction_count; if (!vol_mag_filter_linear || !vol_min_filter_linear) { // Close the linear filtering check. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; } } if (instr.attributes.unnormalized_coordinates || offset_z != 0.0f) { // Handle 3D texture coordinates - may need to normalize and/or add // the offset. Check if 3D. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ELSE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; // Need 1/depth for both normalization and offset. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_RCP) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1)); shader_code_.push_back(size_and_is_3d_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(size_and_is_3d_temp); ++stat_.instruction_count; ++stat_.float_instruction_count; if (instr.attributes.unnormalized_coordinates) { // Normalize the W coordinate. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MUL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(size_and_is_3d_temp); ++stat_.instruction_count; ++stat_.float_instruction_count; } if (offset_z != 0.0f) { // Add normalized offset. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back( *reinterpret_cast(&offset_z)); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(size_and_is_3d_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(coord_temp); ++stat_.instruction_count; ++stat_.float_instruction_count; } } // Close the 3D or stacked check. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; } } } if (instr.opcode == FetchOpcode::kGetTextureWeights) { // Return the fractional part of unnormalized coordinates as bilinear // filtering weights. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_FRC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, coord_mask, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(coord_temp); ++stat_.instruction_count; ++stat_.float_instruction_count; } else { if (instr.dimension == TextureDimension::kCube) { // Convert cubemap coordinates passed as 2D array texture coordinates to // a 3D direction. We can't use a 2D array to emulate cubemaps because // at the edges, especially in pixel shader helper invocations, the // major axis changes, causing S/T to jump between 0 and 1, breaking // gradient calculation and causing the 1x1 mipmap to be sampled. ArrayCoordToCubeDirection(coord_temp); } // Bias the register LOD if fetching with explicit LOD (so this is not // done two or four times due to 3D/stacked and unsigned/signed). uint32_t lod_temp = system_temp_grad_h_lod_, lod_temp_component = 3; if (instr.opcode == FetchOpcode::kTextureFetch && instr.attributes.use_register_lod && instr.attributes.lod_bias != 0.0f) { lod_temp = PushSystemTemp(); lod_temp_component = 0; shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(lod_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(system_temp_grad_h_lod_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back( *reinterpret_cast(&instr.attributes.lod_bias)); ++stat_.instruction_count; ++stat_.float_instruction_count; } // Allocate the register for the value from the signed texture, and also // for the second array layer and lerping the layers. uint32_t signed_value_temp = instr.opcode == FetchOpcode::kTextureFetch ? PushSystemTemp() : UINT32_MAX; uint32_t vol_filter_lerp_temp = UINT32_MAX; if (vol_filter_temp != UINT32_MAX && (!vol_mag_filter_point || !vol_min_filter_point)) { vol_filter_lerp_temp = PushSystemTemp(); } // tfetch1D/2D/Cube just fetch directly. tfetch3D needs to fetch either // the 3D texture or the 2D stacked texture, so two sample instructions // selected conditionally are used in this case. if (instr.dimension == TextureDimension::k3D) { assert_true(size_and_is_3d_temp != UINT32_MAX); shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IF) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN( D3D10_SB_INSTRUCTION_TEST_NONZERO) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1)); shader_code_.push_back(size_and_is_3d_temp); ++stat_.instruction_count; ++stat_.dynamic_flow_control_count; } // Sample both 3D and 2D array bindings for tfetch3D. for (uint32_t i = 0; i < (instr.dimension == TextureDimension::k3D ? 2u : 1u); ++i) { if (i) { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ELSE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; } if (instr.opcode == FetchOpcode::kGetTextureComputedLod) { // The non-pixel-shader case should be handled before because it // just returns a constant in this case. assert_true(IsDxbcPixelShader()); uint32_t srv_register_current = i ? srv_registers_stacked[0] : srv_registers[0]; replicate_result = true; shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_1_SB_OPCODE_LOD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(11)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_RESOURCE, kSwizzleXYZW, 2)); shader_code_.push_back(srv_register_current); shader_code_.push_back(srv_register_current); shader_code_.push_back( EncodeZeroComponentOperand(D3D10_SB_OPERAND_TYPE_SAMPLER, 2)); shader_code_.push_back(sampler_register); shader_code_.push_back(sampler_register); ++stat_.instruction_count; ++stat_.lod_instructions; // Apply the LOD bias if used. if (instr.attributes.lod_bias != 0.0f) { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(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( *reinterpret_cast(&instr.attributes.lod_bias)); ++stat_.instruction_count; ++stat_.float_instruction_count; } } else { // Sample both unsigned and signed, and for stacked textures, two // samples if filtering is needed. for (uint32_t j = 0; j < 2; ++j) { uint32_t srv_register_current = i ? srv_registers_stacked[j] : srv_registers[j]; uint32_t target_temp_sign = j ? signed_value_temp : system_temp_pv_; for (uint32_t k = 0; k < (vol_filter_lerp_temp != UINT32_MAX ? 2u : 1u); ++k) { uint32_t target_temp_current = k ? vol_filter_lerp_temp : target_temp_sign; if (k) { if (!vol_mag_filter_linear || !vol_min_filter_linear) { // Check if array layer filtering is enabled and need one more // sample. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IF) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN( vol_filter_temp_linear_test) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(vol_filter_temp); ++stat_.instruction_count; ++stat_.dynamic_flow_control_count; } // Go to the next array texture sample. 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, 0b0100, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0x3F800000); ++stat_.instruction_count; ++stat_.float_instruction_count; } if (instr.attributes.use_register_lod) { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_SAMPLE_L) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(13)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(target_temp_current); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_RESOURCE, kSwizzleXYZW, 2)); shader_code_.push_back(srv_register_current); shader_code_.push_back(srv_register_current); shader_code_.push_back(EncodeZeroComponentOperand( D3D10_SB_OPERAND_TYPE_SAMPLER, 2)); shader_code_.push_back(sampler_register); shader_code_.push_back(sampler_register); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_TEMP, lod_temp_component, 1)); shader_code_.push_back(lod_temp); ++stat_.instruction_count; ++stat_.texture_normal_instructions; } else if (instr.attributes.use_register_gradients) { // TODO(Triang3l): Apply the LOD bias somehow for register // gradients (possibly will require moving the bias to the // sampler, which may be not very good considering the sampler // count is very limited). shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_SAMPLE_D) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(15)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(target_temp_current); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_RESOURCE, kSwizzleXYZW, 2)); shader_code_.push_back(srv_register_current); shader_code_.push_back(srv_register_current); shader_code_.push_back(EncodeZeroComponentOperand( D3D10_SB_OPERAND_TYPE_SAMPLER, 2)); shader_code_.push_back(sampler_register); shader_code_.push_back(sampler_register); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_grad_h_lod_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_grad_v_); ++stat_.instruction_count; ++stat_.texture_gradient_instructions; } else { // 3 different DXBC opcodes handled here: // - sample_l, when not using a computed LOD or not in a pixel // shader, in this case, LOD (0 + bias) is sampled. // - sample, when sampling in a pixel shader (thus with // derivatives) with a computed LOD. // - sample_b, when sampling in a pixel shader with a biased // computed LOD. // Both sample_l and sample_b should add the LOD bias as the // last operand in our case. bool explicit_lod = !instr.attributes.use_computed_lod || !IsDxbcPixelShader(); if (explicit_lod) { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_SAMPLE_L) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(13)); } else if (instr.attributes.lod_bias != 0.0f) { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_SAMPLE_B) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(13)); } else { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_SAMPLE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(11)); } shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(target_temp_current); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_RESOURCE, kSwizzleXYZW, 2)); shader_code_.push_back(srv_register_current); shader_code_.push_back(srv_register_current); shader_code_.push_back(EncodeZeroComponentOperand( D3D10_SB_OPERAND_TYPE_SAMPLER, 2)); shader_code_.push_back(sampler_register); shader_code_.push_back(sampler_register); if (explicit_lod || instr.attributes.lod_bias != 0.0f) { shader_code_.push_back(EncodeScalarOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(*reinterpret_cast( &instr.attributes.lod_bias)); } ++stat_.instruction_count; if (!explicit_lod && instr.attributes.lod_bias != 0.0f) { ++stat_.texture_bias_instructions; } else { ++stat_.texture_normal_instructions; } } if (k) { // b - a 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, 0b1111, 1)); shader_code_.push_back(target_temp_current); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(target_temp_current); 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(target_temp_sign); ++stat_.instruction_count; ++stat_.float_instruction_count; // a + (b - a) * factor shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back(EncodeVectorMaskedOperand( D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(target_temp_sign); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(target_temp_current); shader_code_.push_back(EncodeVectorReplicatedOperand( D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(vol_filter_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(target_temp_sign); ++stat_.instruction_count; ++stat_.float_instruction_count; if (!j) { // Go back to the first layer to sample the signed texture. 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, 0b0100, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(coord_temp); shader_code_.push_back(EncodeScalarOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0xBF800000u); ++stat_.instruction_count; ++stat_.float_instruction_count; } if (!vol_mag_filter_linear || !vol_min_filter_linear) { // Close the array layer filtering check. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; } } } } } } if (instr.dimension == TextureDimension::k3D) { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; } if (instr.opcode == FetchOpcode::kTextureFetch) { // Will take sign values and exponent bias from the fetch constant. if (cbuffer_index_fetch_constants_ == kCbufferIndexUnallocated) { cbuffer_index_fetch_constants_ = cbuffer_count_++; } assert_true(signed_value_temp != UINT32_MAX); uint32_t sign_temp = PushSystemTemp(); // Choose between channels from the unsigned SRV and the signed one, // apply sign bias (2 * color - 1) and linearize gamma textures. // This is done before applying the exponent bias because biasing and // linearization must be done on color values in 0...1 range, and this // is closer to the storage format, while exponent bias is closer to the // actual usage in shaders. for (uint32_t i = 0; i < 4; ++i) { // Extract the sign values from dword 0 ([0].x or [1].z) of the fetch // constant (in bits 2:9, 2 bits per component) to sign_temp.x. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_UBFE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(11)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(sign_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(2); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(2 + i * 2); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, (tfetch_index & 1) * 2, 3)); shader_code_.push_back(cbuffer_index_fetch_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kFetchConstants)); shader_code_.push_back(tfetch_pair_offset + (tfetch_index & 1)); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Get if the channel is signed to sign_temp.y. 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, 0b0010, 1)); shader_code_.push_back(sign_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(sign_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(uint32_t(TextureSign::kSigned)); ++stat_.instruction_count; ++stat_.int_instruction_count; // Choose between the unsigned and the signed channel. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1 << i, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(sign_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, i, 1)); shader_code_.push_back(signed_value_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, i, 1)); shader_code_.push_back(system_temp_pv_); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Get if the channel is biased to sign_temp.y. 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, 0b0010, 1)); shader_code_.push_back(sign_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(sign_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(uint32_t(TextureSign::kUnsignedBiased)); ++stat_.instruction_count; ++stat_.int_instruction_count; // Expand 0...1 to -1...1 (for normal and DuDv maps, for instance) to // sign_temp.z. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1)); shader_code_.push_back(sign_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, i, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0x40000000u); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0xBF800000u); ++stat_.instruction_count; ++stat_.float_instruction_count; // Choose between the unbiased and the biased channel. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1 << i, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1)); shader_code_.push_back(sign_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1)); shader_code_.push_back(sign_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, i, 1)); shader_code_.push_back(system_temp_pv_); ++stat_.instruction_count; ++stat_.movc_instruction_count; // Get if the channel is in PWL gamma to sign_temp.x. 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(sign_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(sign_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(uint32_t(TextureSign::kGamma)); ++stat_.instruction_count; ++stat_.int_instruction_count; // Check if need to degamma. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IF) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN( D3D10_SB_INSTRUCTION_TEST_NONZERO) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(sign_temp); ++stat_.instruction_count; ++stat_.dynamic_flow_control_count; // Degamma the channel. ConvertPWLGamma(false, system_temp_pv_, i, system_temp_pv_, i, sign_temp, 0, sign_temp, 1); // Close the gamma conditional. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; } // Release sign_temp. PopSystemTemp(); // Apply exponent bias. uint32_t exp_adjust_temp = PushSystemTemp(); // Get the bias value in bits 13:18 of dword 3, which is [0].w or [2].y. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_IBFE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(11)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(exp_adjust_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(6); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(13); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, 3 - 2 * (tfetch_index & 1), 3)); shader_code_.push_back(cbuffer_index_fetch_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kFetchConstants)); shader_code_.push_back(tfetch_pair_offset + (tfetch_index & 1) * 2); ++stat_.instruction_count; ++stat_.uint_instruction_count; // Shift it into float exponent bits. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ISHL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(exp_adjust_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(exp_adjust_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(23); ++stat_.instruction_count; ++stat_.int_instruction_count; // Add this to the exponent of 1.0. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1)); shader_code_.push_back(exp_adjust_temp); shader_code_.push_back( EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(exp_adjust_temp); shader_code_.push_back( EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0)); shader_code_.push_back(0x3F800000); ++stat_.instruction_count; ++stat_.int_instruction_count; // Multiply the value from the texture by 2.0^bias. shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MUL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1)); shader_code_.push_back(exp_adjust_temp); ++stat_.instruction_count; ++stat_.float_instruction_count; // Release exp_adjust_temp. PopSystemTemp(); } if (vol_filter_lerp_temp != UINT32_MAX) { PopSystemTemp(); } if (signed_value_temp != UINT32_MAX) { PopSystemTemp(); } if (lod_temp != system_temp_grad_h_lod_) { PopSystemTemp(); } } if (vol_filter_temp != UINT32_MAX) { PopSystemTemp(); } if (size_and_is_3d_temp != UINT32_MAX) { PopSystemTemp(); } // Release coord_temp. PopSystemTemp(); } else if (instr.opcode == FetchOpcode::kGetTextureGradients) { assert_true(IsDxbcPixelShader()); store_result = true; // pv.xz = ddx(coord.xy) shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_DERIV_RTX_COARSE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3 + operand_length)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0101, 1)); shader_code_.push_back(system_temp_pv_); UseDxbcSourceOperand(operand, 0b01010000); ++stat_.instruction_count; ++stat_.float_instruction_count; // pv.yw = ddy(coord.xy) shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_DERIV_RTY_COARSE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3 + operand_length)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1010, 1)); shader_code_.push_back(system_temp_pv_); UseDxbcSourceOperand(operand, 0b01010000); ++stat_.instruction_count; ++stat_.float_instruction_count; // Get the exponent bias (horizontal in bits 22:26, vertical in bits 27:31 // of dword 4 ([1].x or [2].z) of the fetch constant). if (cbuffer_index_fetch_constants_ == kCbufferIndexUnallocated) { cbuffer_index_fetch_constants_ = cbuffer_count_++; } uint32_t exp_bias_temp = PushSystemTemp(); shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_IBFE) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(17)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0011, 1)); shader_code_.push_back(exp_bias_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(5); shader_code_.push_back(5); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(22); shader_code_.push_back(27); shader_code_.push_back(0); shader_code_.push_back(0); shader_code_.push_back(EncodeVectorReplicatedOperand( D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, (tfetch_index & 1) * 2, 3)); shader_code_.push_back(cbuffer_index_fetch_constants_); shader_code_.push_back(uint32_t(CbufferRegister::kFetchConstants)); shader_code_.push_back(tfetch_pair_offset + 1 + (tfetch_index & 1)); ++stat_.instruction_count; ++stat_.int_instruction_count; // Shift the exponent bias into float exponent bits. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ISHL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0011, 1)); shader_code_.push_back(exp_bias_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(exp_bias_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(23); shader_code_.push_back(23); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.int_instruction_count; // Add the bias to the exponent of 1.0. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IADD) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(10)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0011, 1)); shader_code_.push_back(exp_bias_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(exp_bias_temp); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0)); shader_code_.push_back(0x3F800000); shader_code_.push_back(0x3F800000); shader_code_.push_back(0); shader_code_.push_back(0); ++stat_.instruction_count; ++stat_.int_instruction_count; // Apply the exponent bias. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MUL) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back(EncodeVectorSwizzledOperand( D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1)); shader_code_.push_back(system_temp_pv_); shader_code_.push_back( EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b01000100, 1)); shader_code_.push_back(exp_bias_temp); ++stat_.instruction_count; ++stat_.float_instruction_count; // Release exp_bias_temp. PopSystemTemp(); } else if (instr.opcode == FetchOpcode::kSetTextureLod) { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3 + operand_length)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1000, 1)); shader_code_.push_back(system_temp_grad_h_lod_); UseDxbcSourceOperand(operand, kSwizzleXYZW, 0); ++stat_.instruction_count; ++stat_.mov_instruction_count; } else if (instr.opcode == FetchOpcode::kSetTextureGradientsHorz || instr.opcode == FetchOpcode::kSetTextureGradientsVert) { shader_code_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3 + operand_length)); shader_code_.push_back( EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1)); shader_code_.push_back(instr.opcode == FetchOpcode::kSetTextureGradientsVert ? system_temp_grad_v_ : system_temp_grad_h_lod_); UseDxbcSourceOperand(operand); ++stat_.instruction_count; ++stat_.mov_instruction_count; } if (instr.operand_count >= 1) { UnloadDxbcSourceOperand(operand); } // Re-enter conditional execution if closed it. if (suppress_predication) { // Re-enter exec-level predication. if (cf_exec_predicated_) { D3D10_SB_INSTRUCTION_TEST_BOOLEAN test = cf_exec_predicate_condition_ ? D3D10_SB_INSTRUCTION_TEST_NONZERO : D3D10_SB_INSTRUCTION_TEST_ZERO; shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IF) | ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN(test) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_code_.push_back(EncodeVectorSelectOperand( D3D10_SB_OPERAND_TYPE_TEMP, exec_p0_temp != UINT32_MAX ? 0 : 2, 1)); shader_code_.push_back( exec_p0_temp != UINT32_MAX ? exec_p0_temp : system_temp_ps_pc_p0_a0_); ++stat_.instruction_count; ++stat_.dynamic_flow_control_count; if (exec_p0_temp != UINT32_MAX) { PopSystemTemp(); } } // Update instruction-level predication to the one needed by this tfetch. UpdateInstructionPredication(instr.is_predicated, instr.predicate_condition, false); } if (store_result) { StoreResult(instr.result, system_temp_pv_, replicate_result); } } } // namespace gpu } // namespace xe