/** ****************************************************************************** * 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 "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() {} DxbcShaderTranslator::~DxbcShaderTranslator() = default; void DxbcShaderTranslator::Reset() { ShaderTranslator::Reset(); shader_code_.clear(); rdef_constants_used_ = 0; } std::vector DxbcShaderTranslator::CompleteTranslation() { 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); // 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_aligned, 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::kFloatConstantPageArray, 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, false}, {"xe_bool_loop_cbuffer", RdefConstantIndex::kBoolConstants, 2, 40 * 16, CbufferRegister::kBoolLoopConstants, 1, true}, {"xe_fetch_cbuffer", RdefConstantIndex::kFetchConstants, 1, 48 * 16, CbufferRegister::kFetchConstants, 1, false}, {"xe_float_constants", RdefConstantIndex::kFloatConstants, 1, kFloatConstantsPerPage * 16, CbufferRegister::kFloatConstantsFirst, kFloatConstantPageCount, true}, }; void DxbcShaderTranslator::WriteResourceDefinitions() { uint32_t chunk_position_dwords = uint32_t(shader_object_.size()); uint32_t new_offset; // TODO(Triang3l): Include shared memory, textures, samplers. uint32_t binding_count = uint32_t(RdefConstantBufferIndex::kCount); // *************************************************************************** // 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). shader_object_.push_back(0x500); // 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); } } } // namespace gpu } // namespace xe