/** ****************************************************************************** * 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 "third_party/dxbc/DXBCChecksum.h" #include "third_party/dxbc/d3d12TokenizedProgramFormat.hpp" #include "xenia/base/assert.h" #include "xenia/base/math.h" namespace xe { namespace gpu { using namespace ucode; DxbcShaderTranslator::DxbcShaderTranslator() { // Don't allocate again and again for the first shader. shader_code_.reserve(8192); shader_object_.reserve(16384); } DxbcShaderTranslator::~DxbcShaderTranslator() = default; void DxbcShaderTranslator::Reset() { ShaderTranslator::Reset(); shader_code_.clear(); rdef_constants_used_ = 0; writes_depth_ = false; std::memset(&stat_, 0, sizeof(stat_)); } void DxbcShaderTranslator::CompleteShaderCode() { // Return from `main`. shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_RET) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1)); ++stat_.instruction_count; ++stat_.static_flow_control_count; } std::vector DxbcShaderTranslator::CompleteTranslation() { // Write the code epilogue. CompleteShaderCode(); shader_object_.clear(); // Write the shader object header. shader_object_.push_back('CBXD'); // Checksum (set later). for (uint32_t i = 0; i < 4; ++i) { shader_object_.push_back(0); } shader_object_.push_back(1); // Size (set later). shader_object_.push_back(0); // 5 chunks - RDEF, ISGN, OSGN, SHEX, STAT. shader_object_.push_back(5); // Chunk offsets (set later). for (uint32_t i = 0; i < shader_object_[7]; ++i) { shader_object_.push_back(0); } uint32_t chunk_position_dwords; // Write Resource DEFinitions. chunk_position_dwords = uint32_t(shader_object_.size()); shader_object_[8] = chunk_position_dwords * sizeof(uint32_t); shader_object_.push_back('FEDR'); shader_object_.push_back(0); WriteResourceDefinitions(); shader_object_[chunk_position_dwords + 1] = (uint32_t(shader_object_.size()) - chunk_position_dwords - 2) * sizeof(uint32_t); // Write Input SiGNature. chunk_position_dwords = uint32_t(shader_object_.size()); shader_object_[9] = chunk_position_dwords * sizeof(uint32_t); shader_object_.push_back('NGSI'); shader_object_.push_back(0); WriteInputSignature(); shader_object_[chunk_position_dwords + 1] = (uint32_t(shader_object_.size()) - chunk_position_dwords - 2) * sizeof(uint32_t); // Write Output SiGNature. chunk_position_dwords = uint32_t(shader_object_.size()); shader_object_[10] = chunk_position_dwords * sizeof(uint32_t); shader_object_.push_back('NGSO'); shader_object_.push_back(0); WriteOutputSignature(); shader_object_[chunk_position_dwords + 1] = (uint32_t(shader_object_.size()) - chunk_position_dwords - 2) * sizeof(uint32_t); // Write SHader EXtended. chunk_position_dwords = uint32_t(shader_object_.size()); shader_object_[11] = chunk_position_dwords * sizeof(uint32_t); shader_object_.push_back('XEHS'); shader_object_.push_back(0); WriteShaderCode(); shader_object_[chunk_position_dwords + 1] = (uint32_t(shader_object_.size()) - chunk_position_dwords - 2) * sizeof(uint32_t); // Write STATistics. chunk_position_dwords = uint32_t(shader_object_.size()); shader_object_[12] = chunk_position_dwords * sizeof(uint32_t); shader_object_.push_back('TATS'); shader_object_.push_back(sizeof(stat_)); shader_object_.resize(shader_object_.size() + sizeof(stat_) / sizeof(uint32_t)); std::memcpy(&shader_object_[chunk_position_dwords + 2], &stat_, sizeof(stat_)); // Fill the remaining fields of the header and copy bytes out. uint32_t shader_object_size = uint32_t(shader_object_.size() * sizeof(uint32_t)); shader_object_[6] = shader_object_size; // The checksum includes the size field, so it must be the last. CalculateDXBCChecksum(reinterpret_cast(shader_object_.data()), shader_object_size, reinterpret_cast(&shader_object_[1])); // TODO(Triang3l): Avoid copy? std::vector shader_object_bytes; shader_object_bytes.resize(shader_object_size); std::memcpy(shader_object_bytes.data(), shader_object_.data(), shader_object_size); return shader_object_bytes; } uint32_t DxbcShaderTranslator::AppendString(std::vector& dest, const char* source) { size_t size = std::strlen(source) + 1; size_t size_aligned = xe::align(size, sizeof(uint32_t)); size_t dest_position = dest.size(); dest.resize(dest_position + size_aligned / sizeof(uint32_t)); std::memcpy(&dest[dest_position], source, size); std::memset(reinterpret_cast(&dest[dest_position]) + size, 0xAB, size_aligned - size); return uint32_t(size_aligned); } const DxbcShaderTranslator::RdefStructMember DxbcShaderTranslator::rdef_float_constant_page_member_ = { "c", RdefTypeIndex::kFloatConstantPageArray, 0}; const DxbcShaderTranslator::RdefType DxbcShaderTranslator::rdef_types_[size_t( DxbcShaderTranslator::RdefTypeIndex::kCount)] = { {"float", 0, 3, 1, 1, 0, 0, RdefTypeIndex::kUnknown, nullptr}, {"float2", 1, 3, 1, 2, 0, 0, RdefTypeIndex::kUnknown, nullptr}, {"float3", 1, 3, 1, 3, 0, 0, RdefTypeIndex::kUnknown, nullptr}, {"float4", 1, 3, 1, 4, 0, 0, RdefTypeIndex::kUnknown, nullptr}, {"int", 0, 2, 1, 1, 0, 0, RdefTypeIndex::kUnknown, nullptr}, {"uint", 0, 19, 1, 1, 0, 0, RdefTypeIndex::kUnknown, nullptr}, {"uint4", 1, 19, 1, 4, 0, 0, RdefTypeIndex::kUnknown, nullptr}, {nullptr, 1, 19, 1, 4, 8, 0, RdefTypeIndex::kUint4, nullptr}, {nullptr, 1, 19, 1, 4, 32, 0, RdefTypeIndex::kUint4, nullptr}, {nullptr, 1, 19, 1, 4, 48, 0, RdefTypeIndex::kUint4, nullptr}, {nullptr, 1, 3, 1, 4, kFloatConstantsPerPage, 0, RdefTypeIndex::kFloat4, nullptr}, {"XeFloatConstantPage", 5, 0, 1, kFloatConstantsPerPage * 4, 1, 1, RdefTypeIndex::kUnknown, &rdef_float_constant_page_member_}, }; const DxbcShaderTranslator::RdefConstant DxbcShaderTranslator::rdef_constants_[size_t( DxbcShaderTranslator::RdefConstantIndex::kCount)] = { // SYSTEM CONSTANTS MUST BE UPDATED IF THEIR LAYOUT CHANGES! // System constants vec4 0. {"xe_mul_rcp_w", RdefTypeIndex::kFloat3, 0, 12}, {"xe_vertex_base_index", RdefTypeIndex::kUint, 12, 4}, // System constants vec4 1. {"xe_ndc_scale", RdefTypeIndex::kFloat3, 16, 12}, {"xe_vertex_index_endian", RdefTypeIndex::kUint, 28, 4}, // System constants vec4 2. {"xe_ndc_offset", RdefTypeIndex::kFloat3, 32, 12}, {"xe_pixel_half_pixel_offset", RdefTypeIndex::kFloat, 44, 4}, // System constants vec4 3. {"xe_point_size", RdefTypeIndex::kFloat2, 48, 8}, {"xe_ssaa_inv_scale", RdefTypeIndex::kFloat2, 56, 8}, // System constants vec4 4. {"xe_pixel_pos_reg", RdefTypeIndex::kUint, 64, 4}, {"xe_alpha_test", RdefTypeIndex::kInt, 68, 4}, {"xe_alpha_test_range", RdefTypeIndex::kFloat2, 72, 8}, // System constants vec4 5. {"xe_color_exp_bias", RdefTypeIndex::kFloat4, 80, 16}, // System constants vec4 6. {"xe_color_output_map", RdefTypeIndex::kUint4, 96, 16}, {"xe_bool_constants", RdefTypeIndex::kUint4Array8, 0, 128}, {"xe_loop_constants", RdefTypeIndex::kUint4Array32, 128, 512}, {"xe_fetch_constants", RdefTypeIndex::kUint4Array48, 0, 768}, {"xe_float_constants", RdefTypeIndex::kFloatConstantPageStruct, 0, kFloatConstantsPerPage * 16}, }; const DxbcShaderTranslator::RdefConstantBuffer DxbcShaderTranslator::rdef_constant_buffers_[size_t( DxbcShaderTranslator::RdefConstantBufferIndex::kCount)] = { // SYSTEM CONSTANT SIZE MUST BE UPDATED IF THEIR LAYOUT CHANGES! {"xe_system_cbuffer", RdefConstantIndex::kSystemConstantFirst, uint32_t(RdefConstantIndex::kSystemConstantCount), 112, CbufferRegister::kSystemConstants, 1, true, false}, {"xe_bool_loop_cbuffer", RdefConstantIndex::kBoolConstants, 2, 40 * 16, CbufferRegister::kBoolLoopConstants, 1, true, true}, {"xe_fetch_cbuffer", RdefConstantIndex::kFetchConstants, 1, 48 * 16, CbufferRegister::kFetchConstants, 1, true, false}, {"xe_float_constants", RdefConstantIndex::kFloatConstants, 1, kFloatConstantsPerPage * 16, CbufferRegister::kFloatConstantsFirst, kFloatConstantPageCount, false, true}, }; const DxbcShaderTranslator::RdefConstantBufferIndex DxbcShaderTranslator::constant_buffer_dcl_order_[size_t( DxbcShaderTranslator::RdefConstantBufferIndex::kCount)] = { RdefConstantBufferIndex::kFloatConstants, RdefConstantBufferIndex::kFetchConstants, RdefConstantBufferIndex::kSystemConstants, RdefConstantBufferIndex::kBoolLoopConstants, }; void DxbcShaderTranslator::WriteResourceDefinitions() { uint32_t chunk_position_dwords = uint32_t(shader_object_.size()); uint32_t new_offset; // + 1 for shared memory (vfetches can probably appear in pixel shaders too, // they are handled safely there anyway). // TODO(Triang3l): Textures, samplers. uint32_t binding_count = uint32_t(RdefConstantBufferIndex::kCount) + 1; // *************************************************************************** // Header // *************************************************************************** // Constant buffer count. shader_object_.push_back(uint32_t(RdefConstantBufferIndex::kCount)); // Constant buffer offset (set later). shader_object_.push_back(0); // Bound resource count (CBV, SRV, UAV, samplers). shader_object_.push_back(binding_count); // TODO(Triang3l): Bound resource buffer offset (set later). shader_object_.push_back(0); if (is_vertex_shader()) { // vs_5_1 shader_object_.push_back(0xFFFE0501u); } else { assert_true(is_pixel_shader()); // ps_5_1 shader_object_.push_back(0xFFFF0501u); } // Compiler flags - default for SM 5.1 (no preshader, prefer flow control), // and also skip optimization and IEEE strictness. shader_object_.push_back(0x2504); // Generator offset (directly after the RDEF header in our case). shader_object_.push_back(60); // RD11, but with nibbles inverted (unlike in SM 5.0). shader_object_.push_back(0x25441313); // Unknown fields. shader_object_.push_back(60); shader_object_.push_back(24); // Was 32 in SM 5.0. shader_object_.push_back(40); shader_object_.push_back(40); shader_object_.push_back(36); shader_object_.push_back(12); shader_object_.push_back(0); // Generator name. AppendString(shader_object_, "Xenia"); // *************************************************************************** // Constant types // *************************************************************************** // Type names. new_offset = (uint32_t(shader_object_.size()) - chunk_position_dwords) * sizeof(uint32_t); uint32_t type_name_offsets[size_t(RdefTypeIndex::kCount)]; for (uint32_t i = 0; i < uint32_t(RdefTypeIndex::kCount); ++i) { const RdefType& type = rdef_types_[i]; if (type.name == nullptr) { // Array - use the name of the element type. type_name_offsets[i] = type_name_offsets[uint32_t(type.array_element_type)]; continue; } type_name_offsets[i] = new_offset; new_offset += AppendString(shader_object_, type.name); } // Types. uint32_t types_position_dwords = uint32_t(shader_object_.size()); const uint32_t type_size_dwords = 9; uint32_t types_offset = (types_position_dwords - chunk_position_dwords) * sizeof(uint32_t); const uint32_t type_size = type_size_dwords * sizeof(uint32_t); for (uint32_t i = 0; i < uint32_t(RdefTypeIndex::kCount); ++i) { const RdefType& type = rdef_types_[i]; shader_object_.push_back(type.type_class | (type.type << 16)); shader_object_.push_back(type.row_count | (type.column_count << 16)); shader_object_.push_back(type.element_count | (type.struct_member_count << 16)); // Struct member offset (set later). shader_object_.push_back(0); // Unknown. shader_object_.push_back(0); shader_object_.push_back(0); shader_object_.push_back(0); shader_object_.push_back(0); shader_object_.push_back(type_name_offsets[i]); } // Structure members. for (uint32_t i = 0; i < uint32_t(RdefTypeIndex::kCount); ++i) { const RdefType& type = rdef_types_[i]; const RdefStructMember* struct_members = type.struct_members; if (struct_members == nullptr) { continue; } uint32_t struct_member_position_dwords = uint32_t(shader_object_.size()); shader_object_[types_position_dwords + i * type_size_dwords + 3] = (struct_member_position_dwords - chunk_position_dwords) * sizeof(uint32_t); uint32_t struct_member_count = type.struct_member_count; // Reserve space for names and write types and offsets. for (uint32_t j = 0; j < struct_member_count; ++j) { shader_object_.push_back(0); shader_object_.push_back(types_offset + uint32_t(struct_members[j].type) * type_size); shader_object_.push_back(struct_members[j].offset); } // Write member names. new_offset = (uint32_t(shader_object_.size()) - chunk_position_dwords) * sizeof(uint32_t); for (uint32_t j = 0; j < struct_member_count; ++j) { shader_object_[struct_member_position_dwords + j * 3] = new_offset; new_offset += AppendString(shader_object_, struct_members[j].name); } } // *************************************************************************** // Constants // *************************************************************************** new_offset = (uint32_t(shader_object_.size()) - chunk_position_dwords) * sizeof(uint32_t); uint32_t constant_name_offsets[size_t(RdefConstantIndex::kCount)]; for (uint32_t i = 0; i < uint32_t(RdefConstantIndex::kCount); ++i) { constant_name_offsets[i] = new_offset; new_offset += AppendString(shader_object_, rdef_constants_[i].name); } uint32_t constants_offset = new_offset; const uint32_t constant_size = 40; for (uint32_t i = 0; i < uint32_t(RdefConstantIndex::kCount); ++i) { const RdefConstant& constant = rdef_constants_[i]; shader_object_.push_back(constant_name_offsets[i]); shader_object_.push_back(constant.offset); shader_object_.push_back(constant.size); // Flag 2 is D3D_SVF_USED. shader_object_.push_back((rdef_constants_used_ & (1ull << i)) ? 2 : 0); shader_object_.push_back(types_offset + uint32_t(constant.type) * type_size); // Default value (always 0). shader_object_.push_back(0); // Unknown. shader_object_.push_back(0xFFFFFFFFu); shader_object_.push_back(0); shader_object_.push_back(0xFFFFFFFFu); shader_object_.push_back(0); } // *************************************************************************** // Constant buffers // *************************************************************************** new_offset = (uint32_t(shader_object_.size()) - chunk_position_dwords) * sizeof(uint32_t); uint32_t cbuffer_name_offsets[size_t(RdefConstantBufferIndex::kCount)]; for (uint32_t i = 0; i < uint32_t(RdefConstantBufferIndex::kCount); ++i) { cbuffer_name_offsets[i] = new_offset; new_offset += AppendString(shader_object_, rdef_constant_buffers_[i].name); } // Write the offset to the header. shader_object_[chunk_position_dwords + 1] = new_offset; for (uint32_t i = 0; i < uint32_t(RdefConstantBufferIndex::kCount); ++i) { const RdefConstantBuffer& cbuffer = rdef_constant_buffers_[i]; shader_object_.push_back(cbuffer_name_offsets[i]); shader_object_.push_back(cbuffer.constant_count); shader_object_.push_back(constants_offset + uint32_t(cbuffer.first_constant) * constant_size); shader_object_.push_back(cbuffer.size); // D3D_CT_CBUFFER. shader_object_.push_back(0); // No D3D_SHADER_CBUFFER_FLAGS. shader_object_.push_back(0); } // *************************************************************************** // Bindings, in t#, cb# order // *************************************************************************** // Write used resource names, except for constant buffers because we have // their names already. new_offset = (uint32_t(shader_object_.size()) - chunk_position_dwords) * sizeof(uint32_t); uint32_t shared_memory_name_offset = new_offset; new_offset += AppendString(shader_object_, "xe_shared_memory"); // TODO(Triang3l): Texture and sampler names. // Write the offset to the header. shader_object_[chunk_position_dwords + 3] = new_offset; // Shared memory. shader_object_.push_back(shared_memory_name_offset); // D3D_SIT_BYTEADDRESS. shader_object_.push_back(7); // D3D_RETURN_TYPE_MIXED. shader_object_.push_back(6); // D3D_SRV_DIMENSION_UNKNOWN. shader_object_.push_back(1); // Not multisampled. shader_object_.push_back(0); // Register t0. shader_object_.push_back(0); // One binding. shader_object_.push_back(1); // No D3D_SHADER_INPUT_FLAGS. shader_object_.push_back(0); // Register space 0. shader_object_.push_back(0); // SRV ID T0. shader_object_.push_back(0); // TODO(Triang3l): Textures and samplers. // Constant buffers. for (uint32_t i = 0; i < uint32_t(RdefConstantBufferIndex::kCount); ++i) { const RdefConstantBuffer& cbuffer = rdef_constant_buffers_[i]; shader_object_.push_back(cbuffer_name_offsets[i]); // D3D_SIT_CBUFFER. shader_object_.push_back(0); // No D3D_RESOURCE_RETURN_TYPE. shader_object_.push_back(0); // D3D_SRV_DIMENSION_UNKNOWN (not an SRV). shader_object_.push_back(0); shader_object_.push_back(0); shader_object_.push_back(uint32_t(cbuffer.register_index)); shader_object_.push_back(cbuffer.binding_count); // D3D_SIF_USERPACKED if a `cbuffer` rather than a `ConstantBuffer`. shader_object_.push_back(cbuffer.user_packed ? 1 : 0); // Register space 0. shader_object_.push_back(0); shader_object_.push_back(i); } } void DxbcShaderTranslator::WriteInputSignature() { uint32_t chunk_position_dwords = uint32_t(shader_object_.size()); uint32_t new_offset; const uint32_t signature_position_dwords = 2; const uint32_t signature_size_dwords = 6; if (is_vertex_shader()) { // Only unswapped vertex index. shader_object_.push_back(1); // Unknown. shader_object_.push_back(8); // Vertex index. // Semantic name SV_VertexID (the only one in the signature). shader_object_.push_back( (signature_position_dwords + signature_size_dwords) * sizeof(uint32_t)); // Semantic index. shader_object_.push_back(0); // D3D_NAME_VERTEX_ID. shader_object_.push_back(6); // D3D_REGISTER_COMPONENT_UINT32. shader_object_.push_back(1); shader_object_.push_back(kVSInVertexIndexRegister); // x present, x used (always written to GPR 0). shader_object_.push_back(0x1 | (0x1 << 8)); // Vertex index semantic name. AppendString(shader_object_, "SV_VertexID"); } else { assert_true(is_pixel_shader()); // Interpolators, point parameters (coordinates, size), screen position. shader_object_.push_back(kInterpolatorCount + 2); // Unknown. shader_object_.push_back(8); // Intepolators. for (uint32_t i = 0; i < kInterpolatorCount; ++i) { // Reserve space for the semantic name (TEXCOORD). shader_object_.push_back(0); shader_object_.push_back(i); // D3D_NAME_UNDEFINED. shader_object_.push_back(0); // D3D_REGISTER_COMPONENT_FLOAT32. shader_object_.push_back(3); shader_object_.push_back(kPSInInterpolatorRegister + i); // Interpolators are copied to GPRs in the beginning of the shader. If // there's a register to copy to, this interpolator is used. shader_object_.push_back(0xF | (i < register_count() ? (0xF << 8) : 0)); } // Point parameters - coordinate on the point and point size as a float3 // TEXCOORD (but the size in Z is not needed). Always used because // ps_param_gen is handled dynamically. shader_object_.push_back(0); shader_object_.push_back(kPointParametersTexCoord); shader_object_.push_back(0); shader_object_.push_back(3); shader_object_.push_back(kPSInPointParametersRegister); shader_object_.push_back(0x7 | (0x3 << 8)); // Position (only XY needed). Always used because ps_param_gen is handled // dynamically. shader_object_.push_back(0); shader_object_.push_back(0); // D3D_NAME_POSITION. shader_object_.push_back(1); shader_object_.push_back(3); shader_object_.push_back(kPSInPositionRegister); shader_object_.push_back(0xF | (0x3 << 8)); // Write the semantic names. new_offset = (uint32_t(shader_object_.size()) - chunk_position_dwords) * sizeof(uint32_t); for (uint32_t i = 0; i < kInterpolatorCount + 1; ++i) { uint32_t texcoord_name_position_dwords = chunk_position_dwords + signature_position_dwords + i * signature_size_dwords; shader_object_[texcoord_name_position_dwords] = new_offset; } new_offset += AppendString(shader_object_, "TEXCOORD"); uint32_t position_name_position_dwords = chunk_position_dwords + signature_position_dwords + (kInterpolatorCount + 1) * signature_size_dwords; shader_object_[position_name_position_dwords] = new_offset; new_offset += AppendString(shader_object_, "SV_Position"); } } void DxbcShaderTranslator::WriteOutputSignature() { uint32_t chunk_position_dwords = uint32_t(shader_object_.size()); uint32_t new_offset; const uint32_t signature_position_dwords = 2; const uint32_t signature_size_dwords = 6; if (is_vertex_shader()) { // Interpolators, point parameters (coordinates, size), screen position. shader_object_.push_back(kInterpolatorCount + 2); // Unknown. shader_object_.push_back(8); // Intepolators. for (uint32_t i = 0; i < kInterpolatorCount; ++i) { // Reserve space for the semantic name (TEXCOORD). shader_object_.push_back(0); // Semantic index. shader_object_.push_back(i); // D3D_NAME_UNDEFINED. shader_object_.push_back(0); // D3D_REGISTER_COMPONENT_FLOAT32. shader_object_.push_back(3); shader_object_.push_back(kVSOutInterpolatorRegister + i); // Unlike in ISGN, the second byte contains the unused components, not the // used ones. All components are always used because they are reset to 0. shader_object_.push_back(0xF); } // Point parameters - coordinate on the point and point size as a float3 // TEXCOORD. Always used because reset to (0, 0, -1). shader_object_.push_back(0); shader_object_.push_back(kPointParametersTexCoord); shader_object_.push_back(0); shader_object_.push_back(3); shader_object_.push_back(kVSOutPointParametersRegister); shader_object_.push_back(0x7 | (0x8 << 8)); // Position. shader_object_.push_back(0); shader_object_.push_back(0); // D3D_NAME_POSITION. shader_object_.push_back(1); shader_object_.push_back(3); shader_object_.push_back(kVSOutPositionRegister); shader_object_.push_back(0xF); // Write the semantic names. new_offset = (uint32_t(shader_object_.size()) - chunk_position_dwords) * sizeof(uint32_t); for (uint32_t i = 0; i < kInterpolatorCount + 1; ++i) { uint32_t texcoord_name_position_dwords = chunk_position_dwords + signature_position_dwords + i * signature_size_dwords; shader_object_[texcoord_name_position_dwords] = new_offset; } new_offset += AppendString(shader_object_, "TEXCOORD"); uint32_t position_name_position_dwords = chunk_position_dwords + signature_position_dwords + (kInterpolatorCount + 1) * signature_size_dwords; shader_object_[position_name_position_dwords] = new_offset; new_offset += AppendString(shader_object_, "SV_Position"); } else { assert_true(is_pixel_shader()); // Color render targets, optionally depth. shader_object_.push_back(4 + (writes_depth_ ? 1 : 0)); // Unknown. shader_object_.push_back(8); // Color render targets. for (uint32_t i = 0; i < 4; ++i) { // Reserve space for the semantic name (SV_Target). shader_object_.push_back(0); shader_object_.push_back(i); // D3D_NAME_UNDEFINED for some reason - this is correct. shader_object_.push_back(0); shader_object_.push_back(3); // Register must match the render target index. shader_object_.push_back(i); // All are used because X360 RTs are dynamically remapped to D3D12 RTs to // make the indices consecutive. shader_object_.push_back(0xF); } // Depth. if (writes_depth_) { // Reserve space for the semantic name (SV_Depth). shader_object_.push_back(0); shader_object_.push_back(0); shader_object_.push_back(0); shader_object_.push_back(3); shader_object_.push_back(0xFFFFFFFFu); shader_object_.push_back(0x1 | (0xE << 8)); } // Write the semantic names. new_offset = (uint32_t(shader_object_.size()) - chunk_position_dwords) * sizeof(uint32_t); for (uint32_t i = 0; i < 4; ++i) { uint32_t color_name_position_dwords = chunk_position_dwords + signature_position_dwords + i * signature_size_dwords; shader_object_[color_name_position_dwords] = new_offset; } new_offset += AppendString(shader_object_, "SV_Target"); if (writes_depth_) { uint32_t depth_name_position_dwords = chunk_position_dwords + signature_position_dwords + 4 * signature_size_dwords; shader_object_[depth_name_position_dwords] = new_offset; new_offset += AppendString(shader_object_, "SV_Depth"); } } } void DxbcShaderTranslator::WriteShaderCode() { uint32_t chunk_position_dwords = uint32_t(shader_object_.size()); D3D10_SB_TOKENIZED_PROGRAM_TYPE program_type = is_vertex_shader() ? D3D10_SB_VERTEX_SHADER : D3D10_SB_PIXEL_SHADER; shader_object_.push_back( ENCODE_D3D10_SB_TOKENIZED_PROGRAM_VERSION_TOKEN(program_type, 5, 1)); // Reserve space for the length token. shader_object_.push_back(0); // Declarations (don't increase the instruction count stat, and only inputs // and outputs are counted in dcl_count). // Binding declarations have 3D-indexed operands with XYZW swizzle, the first // index being the binding ID (local to the shader), the second being the // lower register index bound, and the third being the highest register index // bound. // Inputs/outputs have 1D-indexed operands with a component mask and a // register index. const uint32_t binding_operand_token = ENCODE_D3D10_SB_OPERAND_NUM_COMPONENTS(D3D10_SB_OPERAND_4_COMPONENT) | ENCODE_D3D10_SB_OPERAND_4_COMPONENT_SELECTION_MODE( D3D10_SB_OPERAND_4_COMPONENT_SWIZZLE_MODE) | D3D10_SB_OPERAND_4_COMPONENT_NOSWIZZLE | ENCODE_D3D10_SB_OPERAND_INDEX_DIMENSION(D3D10_SB_OPERAND_INDEX_3D) | ENCODE_D3D10_SB_OPERAND_INDEX_REPRESENTATION( 0, D3D10_SB_OPERAND_INDEX_IMMEDIATE32) | ENCODE_D3D10_SB_OPERAND_INDEX_REPRESENTATION( 1, D3D10_SB_OPERAND_INDEX_IMMEDIATE32) | ENCODE_D3D10_SB_OPERAND_INDEX_REPRESENTATION( 2, D3D10_SB_OPERAND_INDEX_IMMEDIATE32); const uint32_t input_operand_unmasked_token = ENCODE_D3D10_SB_OPERAND_NUM_COMPONENTS(D3D10_SB_OPERAND_4_COMPONENT) | ENCODE_D3D10_SB_OPERAND_4_COMPONENT_SELECTION_MODE( D3D10_SB_OPERAND_4_COMPONENT_MASK_MODE) | ENCODE_D3D10_SB_OPERAND_TYPE(D3D10_SB_OPERAND_TYPE_INPUT) | ENCODE_D3D10_SB_OPERAND_INDEX_DIMENSION(D3D10_SB_OPERAND_INDEX_1D) | ENCODE_D3D10_SB_OPERAND_INDEX_REPRESENTATION( 0, D3D10_SB_OPERAND_INDEX_IMMEDIATE32); const uint32_t output_operand_unmasked_token = ENCODE_D3D10_SB_OPERAND_NUM_COMPONENTS(D3D10_SB_OPERAND_4_COMPONENT) | ENCODE_D3D10_SB_OPERAND_4_COMPONENT_SELECTION_MODE( D3D10_SB_OPERAND_4_COMPONENT_MASK_MODE) | ENCODE_D3D10_SB_OPERAND_TYPE(D3D10_SB_OPERAND_TYPE_OUTPUT) | ENCODE_D3D10_SB_OPERAND_INDEX_DIMENSION(D3D10_SB_OPERAND_INDEX_1D) | ENCODE_D3D10_SB_OPERAND_INDEX_REPRESENTATION( 0, D3D10_SB_OPERAND_INDEX_IMMEDIATE32); // Don't allow refactoring when converting to native code to maintain position // invariance (needed even in pixel shaders for oDepth invariance). shader_object_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_GLOBAL_FLAGS) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1) | D3D11_1_SB_GLOBAL_FLAG_SKIP_OPTIMIZATION); // Constant buffers. for (uint32_t i = 0; i < uint32_t(RdefConstantBufferIndex::kCount); ++i) { uint32_t cbuffer_index = uint32_t(constant_buffer_dcl_order_[i]); const RdefConstantBuffer& cbuffer = rdef_constant_buffers_[cbuffer_index]; shader_object_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_CONSTANT_BUFFER) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7) | ENCODE_D3D10_SB_D3D10_SB_CONSTANT_BUFFER_ACCESS_PATTERN( cbuffer.dynamic_indexed ? D3D10_SB_CONSTANT_BUFFER_DYNAMIC_INDEXED : D3D10_SB_CONSTANT_BUFFER_IMMEDIATE_INDEXED)); shader_object_.push_back( binding_operand_token | ENCODE_D3D10_SB_OPERAND_TYPE(D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER)); shader_object_.push_back(cbuffer_index); shader_object_.push_back(uint32_t(cbuffer.register_index)); shader_object_.push_back(uint32_t(cbuffer.register_index) + cbuffer.binding_count - 1); shader_object_.push_back((cbuffer.size + 15) >> 4); // Space 0. shader_object_.push_back(0); } // Shader resources. // Shared memory ByteAddressBuffer (T0, at t0, space0). shader_object_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_DCL_RESOURCE_RAW) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(6)); shader_object_.push_back( binding_operand_token | ENCODE_D3D10_SB_OPERAND_TYPE(D3D10_SB_OPERAND_TYPE_RESOURCE)); shader_object_.push_back(0); shader_object_.push_back(0); shader_object_.push_back(0); shader_object_.push_back(0); // Inputs and outputs. if (is_vertex_shader()) { // Unswapped vertex index input (only X component). shader_object_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_INPUT_SGV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(4)); shader_object_.push_back(input_operand_unmasked_token | D3D10_SB_OPERAND_4_COMPONENT_MASK_X); shader_object_.push_back(kVSInVertexIndexRegister); shader_object_.push_back(ENCODE_D3D10_SB_NAME(D3D10_SB_NAME_VERTEX_ID)); ++stat_.dcl_count; // Interpolator output. for (uint32_t i = 0; i < kInterpolatorCount; ++i) { shader_object_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_OUTPUT) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_object_.push_back(output_operand_unmasked_token | D3D10_SB_OPERAND_4_COMPONENT_MASK_ALL); shader_object_.push_back(kVSOutInterpolatorRegister + i); ++stat_.dcl_count; } // Point parameters output. shader_object_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_OUTPUT) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_object_.push_back(output_operand_unmasked_token | D3D10_SB_OPERAND_4_COMPONENT_MASK_X | D3D10_SB_OPERAND_4_COMPONENT_MASK_Y | D3D10_SB_OPERAND_4_COMPONENT_MASK_Z); shader_object_.push_back(kVSOutPointParametersRegister); ++stat_.dcl_count; // Position output. shader_object_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_OUTPUT_SIV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(4)); shader_object_.push_back(output_operand_unmasked_token | D3D10_SB_OPERAND_4_COMPONENT_MASK_ALL); shader_object_.push_back(kVSOutPositionRegister); shader_object_.push_back(ENCODE_D3D10_SB_NAME(D3D10_SB_NAME_POSITION)); ++stat_.dcl_count; } else if (is_pixel_shader()) { // Interpolator input. uint32_t interpolator_count = std::min(kInterpolatorCount, register_count()); for (uint32_t i = 0; i < interpolator_count; ++i) { shader_object_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_INPUT_PS) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3) | ENCODE_D3D10_SB_INPUT_INTERPOLATION_MODE( D3D10_SB_INTERPOLATION_LINEAR)); shader_object_.push_back(input_operand_unmasked_token | D3D10_SB_OPERAND_4_COMPONENT_MASK_ALL); shader_object_.push_back(kPSInInterpolatorRegister + i); ++stat_.dcl_count; } // Point parameters input (only coordinates, not size, needed). shader_object_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_INPUT_PS) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3) | ENCODE_D3D10_SB_INPUT_INTERPOLATION_MODE( D3D10_SB_INTERPOLATION_LINEAR)); shader_object_.push_back(input_operand_unmasked_token | D3D10_SB_OPERAND_4_COMPONENT_MASK_X | D3D10_SB_OPERAND_4_COMPONENT_MASK_Y); shader_object_.push_back(kPSInPointParametersRegister); ++stat_.dcl_count; // Position input (only XY needed). shader_object_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_INPUT_PS_SIV) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(4) | ENCODE_D3D10_SB_INPUT_INTERPOLATION_MODE( D3D10_SB_INTERPOLATION_LINEAR_NOPERSPECTIVE)); shader_object_.push_back(input_operand_unmasked_token | D3D10_SB_OPERAND_4_COMPONENT_MASK_X | D3D10_SB_OPERAND_4_COMPONENT_MASK_Y); shader_object_.push_back(kPSInPositionRegister); shader_object_.push_back(ENCODE_D3D10_SB_NAME(D3D10_SB_NAME_POSITION)); ++stat_.dcl_count; // Color output. for (uint32_t i = 0; i < kInterpolatorCount; ++i) { shader_object_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_OUTPUT) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3)); shader_object_.push_back(output_operand_unmasked_token | D3D10_SB_OPERAND_4_COMPONENT_MASK_ALL); shader_object_.push_back(i); ++stat_.dcl_count; } // Depth output. if (writes_depth_) { shader_object_.push_back( ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_OUTPUT) | ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(2)); shader_object_.push_back( ENCODE_D3D10_SB_OPERAND_NUM_COMPONENTS(D3D10_SB_OPERAND_1_COMPONENT) | ENCODE_D3D10_SB_OPERAND_TYPE(D3D10_SB_OPERAND_TYPE_OUTPUT_DEPTH) | ENCODE_D3D10_SB_OPERAND_INDEX_DIMENSION(D3D10_SB_OPERAND_INDEX_0D)); ++stat_.dcl_count; } } // Write the translated shader code. size_t code_size_dwords = shader_code_.size(); // So [] won't crash in case the size is zero somehow. if (code_size_dwords != 0) { shader_object_.resize(shader_object_.size() + code_size_dwords); std::memcpy(&shader_object_[shader_object_.size() - code_size_dwords], shader_code_.data(), code_size_dwords * sizeof(uint32_t)); } // Write the length. shader_object_[chunk_position_dwords + 1] = uint32_t(shader_object_.size()) - chunk_position_dwords; } } // namespace gpu } // namespace xe