Files
Xenia-Canary/src/xenia/gpu/dxbc_shader_translator.cc
2018-08-29 19:46:38 +03:00

335 lines
14 KiB
C++

/**
******************************************************************************
* 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 <cstring>
#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<uint8_t> 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<unsigned char*>(shader_object_.data()),
shader_object_size,
reinterpret_cast<unsigned int*>(&shader_object_[1]));
// TODO(Triang3l): Avoid copy?
std::vector<uint8_t> 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<uint32_t>& 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<uint8_t*>(&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