4768 lines
201 KiB
C++
4768 lines
201 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 <algorithm>
|
|
#include <cstring>
|
|
#include <memory>
|
|
|
|
#include "third_party/dxbc/DXBCChecksum.h"
|
|
#include "third_party/dxbc/d3d12TokenizedProgramFormat.hpp"
|
|
|
|
#include "xenia/base/assert.h"
|
|
#include "xenia/base/cvar.h"
|
|
|
|
DEFINE_bool(dxbc_switch, true,
|
|
"Use switch rather than if for flow control. Turning this off or "
|
|
"on may improve stability, though this heavily depends on the "
|
|
"driver - on AMD, it's recommended to have this set to true, as "
|
|
"Halo 3 appears to crash when if is used for flow control "
|
|
"(possibly the shader compiler tries to flatten them). On Intel "
|
|
"HD Graphics, this is ignored because of a crash with the switch "
|
|
"instruction.",
|
|
"D3D12");
|
|
DEFINE_bool(dxbc_source_map, false,
|
|
"Disassemble Xenos instructions as comments in the resulting DXBC "
|
|
"for debugging.",
|
|
"D3D12");
|
|
|
|
namespace xe {
|
|
namespace gpu {
|
|
using namespace ucode;
|
|
|
|
// Notes about operands:
|
|
//
|
|
// Reading and writing:
|
|
// - Writes to 4-component registers must be masked.
|
|
// - Reads from 4-component registers can be swizzled, or 1 component can be
|
|
// selected.
|
|
// - r# (temporary registers) are 4-component and can be used anywhere.
|
|
// - v# (inputs) are 4-component and read-only.
|
|
// - o# (outputs) are 4-component and write-only.
|
|
// - oDepth (pixel shader depth output) is 1-component and write-only.
|
|
// - x# (indexable temporary registers) are 4-component (though not sure what
|
|
// happens if you dcl them as 1-component) and can be accessed either via
|
|
// a mov load or a mov store (and those movs are counted as ArrayInstructions
|
|
// in STAT, not as MovInstructions).
|
|
//
|
|
// Indexing:
|
|
// - Constant buffers use 3D indices in CBx[y][z] format, where x is the ID of
|
|
// the binding (CB#), y is the register to access within its space, z is the
|
|
// 4-component vector to access within the register binding.
|
|
// For example, if the requested vector is located in the beginning of the
|
|
// second buffer in the descriptor array at b2, which is assigned to CB1, the
|
|
// index would be CB1[3][0].
|
|
// - Resources and samplers use 2D indices, where the first dimension is the
|
|
// S#/T#/U# binding index, and the second is the s#/t#/u# register index
|
|
// within its space.
|
|
|
|
constexpr uint32_t DxbcShaderTranslator::kMaxTextureSRVIndexBits;
|
|
constexpr uint32_t DxbcShaderTranslator::kMaxTextureSRVs;
|
|
constexpr uint32_t DxbcShaderTranslator::kMaxSamplerBindingIndexBits;
|
|
constexpr uint32_t DxbcShaderTranslator::kMaxSamplerBindings;
|
|
constexpr uint32_t DxbcShaderTranslator::kInterpolatorCount;
|
|
constexpr uint32_t DxbcShaderTranslator::kPointParametersTexCoord;
|
|
constexpr uint32_t DxbcShaderTranslator::kClipSpaceZWTexCoord;
|
|
constexpr uint32_t DxbcShaderTranslator::kSwizzleXYZW;
|
|
constexpr uint32_t DxbcShaderTranslator::kSwizzleXXXX;
|
|
constexpr uint32_t DxbcShaderTranslator::kSwizzleYYYY;
|
|
constexpr uint32_t DxbcShaderTranslator::kSwizzleZZZZ;
|
|
constexpr uint32_t DxbcShaderTranslator::kSwizzleWWWW;
|
|
constexpr uint32_t
|
|
DxbcShaderTranslator::DxbcSourceOperand::kIntermediateRegisterNone;
|
|
constexpr uint32_t DxbcShaderTranslator::kCbufferIndexUnallocated;
|
|
constexpr uint32_t DxbcShaderTranslator::kCfExecBoolConstantNone;
|
|
|
|
DxbcShaderTranslator::DxbcShaderTranslator(uint32_t vendor_id,
|
|
bool edram_rov_used)
|
|
: vendor_id_(vendor_id), edram_rov_used_(edram_rov_used) {
|
|
// Don't allocate again and again for the first shader.
|
|
shader_code_.reserve(8192);
|
|
shader_object_.reserve(16384);
|
|
float_constant_index_offsets_.reserve(512);
|
|
}
|
|
DxbcShaderTranslator::~DxbcShaderTranslator() = default;
|
|
|
|
std::vector<uint8_t> DxbcShaderTranslator::ForceEarlyDepthStencil(
|
|
const uint8_t* shader) {
|
|
const uint32_t* old_shader = reinterpret_cast<const uint32_t*>(shader);
|
|
|
|
// To return something anyway even if patching fails.
|
|
std::vector<uint8_t> new_shader;
|
|
uint32_t shader_size_bytes = old_shader[6];
|
|
new_shader.resize(shader_size_bytes);
|
|
std::memcpy(new_shader.data(), shader, shader_size_bytes);
|
|
|
|
// Find the SHEX chunk.
|
|
uint32_t chunk_count = old_shader[7];
|
|
for (uint32_t i = 0; i < chunk_count; ++i) {
|
|
uint32_t chunk_offset_bytes = old_shader[8 + i];
|
|
const uint32_t* chunk = old_shader + chunk_offset_bytes / sizeof(uint32_t);
|
|
if (chunk[0] != 'XEHS') {
|
|
continue;
|
|
}
|
|
// Find dcl_globalFlags and patch it.
|
|
uint32_t code_size_dwords = chunk[3];
|
|
chunk += 4;
|
|
for (uint32_t j = 0; j < code_size_dwords;) {
|
|
uint32_t opcode_token = chunk[j];
|
|
uint32_t opcode = DECODE_D3D10_SB_OPCODE_TYPE(opcode_token);
|
|
if (opcode == D3D10_SB_OPCODE_DCL_GLOBAL_FLAGS) {
|
|
opcode_token |= D3D11_SB_GLOBAL_FLAG_FORCE_EARLY_DEPTH_STENCIL;
|
|
std::memcpy(new_shader.data() +
|
|
(chunk_offset_bytes + (4 + j) * sizeof(uint32_t)),
|
|
&opcode_token, sizeof(uint32_t));
|
|
// Recalculate the checksum since the shader was modified.
|
|
CalculateDXBCChecksum(
|
|
reinterpret_cast<unsigned char*>(new_shader.data()),
|
|
shader_size_bytes,
|
|
reinterpret_cast<unsigned int*>(new_shader.data() +
|
|
sizeof(uint32_t)));
|
|
break;
|
|
}
|
|
if (opcode == D3D10_SB_OPCODE_CUSTOMDATA) {
|
|
j += chunk[j + 1];
|
|
} else {
|
|
j += DECODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(opcode_token);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return std::move(new_shader);
|
|
}
|
|
|
|
std::vector<uint8_t> DxbcShaderTranslator::CreateDepthOnlyPixelShader() {
|
|
Reset();
|
|
is_depth_only_pixel_shader_ = true;
|
|
StartTranslation();
|
|
return std::move(CompleteTranslation());
|
|
}
|
|
|
|
void DxbcShaderTranslator::Reset() {
|
|
ShaderTranslator::Reset();
|
|
|
|
shader_code_.clear();
|
|
|
|
is_depth_only_pixel_shader_ = false;
|
|
|
|
cbuffer_count_ = 0;
|
|
// System constants always used in prologues/epilogues.
|
|
cbuffer_index_system_constants_ = cbuffer_count_++;
|
|
cbuffer_index_float_constants_ = kCbufferIndexUnallocated;
|
|
cbuffer_index_bool_loop_constants_ = kCbufferIndexUnallocated;
|
|
cbuffer_index_fetch_constants_ = kCbufferIndexUnallocated;
|
|
|
|
system_constants_used_ = 0;
|
|
float_constants_dynamic_indexed_ = false;
|
|
float_constant_index_offsets_.clear();
|
|
|
|
system_temp_count_current_ = 0;
|
|
system_temp_count_max_ = 0;
|
|
|
|
cf_exec_bool_constant_ = kCfExecBoolConstantNone;
|
|
cf_exec_predicated_ = false;
|
|
cf_instruction_predicate_if_open_ = false;
|
|
cf_exec_predicate_written_ = false;
|
|
|
|
texture_srvs_.clear();
|
|
sampler_bindings_.clear();
|
|
|
|
memexport_alloc_current_count_ = 0;
|
|
|
|
std::memset(&stat_, 0, sizeof(stat_));
|
|
}
|
|
|
|
void DxbcShaderTranslator::DxbcSrc::Write(std::vector<uint32_t>& code,
|
|
uint32_t dest_write_mask,
|
|
bool is_integer) const {
|
|
uint32_t operand_token = GetOperandTokenTypeAndIndex();
|
|
uint32_t dest_component = DxbcDest::GetMaskSingleComponent(dest_write_mask);
|
|
uint32_t select_component = dest_component != UINT32_MAX ? dest_component : 0;
|
|
bool dest_is_vector =
|
|
dest_write_mask != 0b0000 && dest_component == UINT32_MAX;
|
|
if (type_ == DxbcOperandType::kImmediate32) {
|
|
if (dest_is_vector) {
|
|
operand_token |= uint32_t(DxbcOperandDimension::kVector) |
|
|
(uint32_t(DxbcComponentSelection::kSwizzle) << 2) |
|
|
(DxbcSrc::kXYZW << 4);
|
|
} else {
|
|
operand_token |= uint32_t(DxbcOperandDimension::kScalar);
|
|
}
|
|
code.push_back(operand_token);
|
|
if (dest_is_vector) {
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
code.push_back((dest_write_mask & (1 << i))
|
|
? GetModifiedImmediate(i, is_integer)
|
|
: 0);
|
|
}
|
|
} else {
|
|
code.push_back(GetModifiedImmediate(select_component, is_integer));
|
|
}
|
|
} else {
|
|
switch (GetDimension()) {
|
|
case DxbcOperandDimension::kScalar:
|
|
if (dest_is_vector) {
|
|
operand_token |= uint32_t(DxbcOperandDimension::kVector) |
|
|
(uint32_t(DxbcComponentSelection::kSwizzle) << 2) |
|
|
(DxbcSrc::kXXXX << 4);
|
|
} else {
|
|
operand_token |= uint32_t(DxbcOperandDimension::kScalar);
|
|
}
|
|
break;
|
|
case DxbcOperandDimension::kVector:
|
|
operand_token |= uint32_t(DxbcOperandDimension::kVector);
|
|
if (dest_is_vector) {
|
|
operand_token |= uint32_t(DxbcComponentSelection::kSwizzle) << 2;
|
|
// Clear swizzle of unused components to a used value to avoid
|
|
// referencing potentially uninitialized register components.
|
|
uint32_t used_component;
|
|
if (!xe::bit_scan_forward(dest_write_mask, &used_component)) {
|
|
used_component = 0;
|
|
}
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
uint32_t swizzle_index =
|
|
(dest_write_mask & (1 << i)) ? i : used_component;
|
|
operand_token |=
|
|
(((swizzle_ >> (swizzle_index * 2)) & 3) << (4 + i * 2));
|
|
}
|
|
} else {
|
|
operand_token |= (uint32_t(DxbcComponentSelection::kSelect1) << 2) |
|
|
(((swizzle_ >> (select_component * 2)) & 3) << 4);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
DxbcOperandModifier modifier = DxbcOperandModifier::kNone;
|
|
if (absolute_ && negate_) {
|
|
modifier = DxbcOperandModifier::kAbsoluteNegate;
|
|
} else if (absolute_) {
|
|
modifier = DxbcOperandModifier::kAbsolute;
|
|
} else if (negate_) {
|
|
modifier = DxbcOperandModifier::kNegate;
|
|
}
|
|
if (modifier != DxbcOperandModifier::kNone) {
|
|
operand_token |= uint32_t(1) << 31;
|
|
}
|
|
code.push_back(operand_token);
|
|
if (modifier != DxbcOperandModifier::kNone) {
|
|
code.push_back(uint32_t(DxbcExtendedOperandType::kModifier) |
|
|
(uint32_t(modifier) << 6));
|
|
}
|
|
DxbcOperandAddress::Write(code);
|
|
}
|
|
}
|
|
|
|
bool DxbcShaderTranslator::UseSwitchForControlFlow() const {
|
|
// Xenia crashes on Intel HD Graphics 4000 with switch.
|
|
return cvars::dxbc_switch && vendor_id_ != 0x8086;
|
|
}
|
|
|
|
uint32_t DxbcShaderTranslator::PushSystemTemp(uint32_t zero_mask,
|
|
uint32_t count) {
|
|
uint32_t register_index = system_temp_count_current_;
|
|
if (!uses_register_dynamic_addressing() && !is_depth_only_pixel_shader_) {
|
|
// Guest shader registers first if they're not in x0. Depth-only pixel
|
|
// shader is a special case of the DXBC translator usage, where there are no
|
|
// GPRs because there's no shader to translate, and a guest shader is not
|
|
// loaded.
|
|
register_index += register_count();
|
|
}
|
|
system_temp_count_current_ += count;
|
|
system_temp_count_max_ =
|
|
std::max(system_temp_count_max_, system_temp_count_current_);
|
|
|
|
if (zero_mask) {
|
|
uint32_t zero_operand, zero_count;
|
|
if (zero_mask == 0b0001 || zero_mask == 0b0010 || zero_mask == 0b0100 ||
|
|
zero_mask == 0b1000) {
|
|
zero_operand = EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0);
|
|
zero_count = 1;
|
|
} else {
|
|
zero_operand = EncodeVectorSwizzledOperand(
|
|
D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0);
|
|
zero_count = 4;
|
|
}
|
|
for (uint32_t i = 0; i < count; ++i) {
|
|
shader_code_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(4 + zero_count));
|
|
shader_code_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, zero_mask, 1));
|
|
shader_code_.push_back(register_index + i);
|
|
shader_code_.push_back(zero_operand);
|
|
for (uint32_t j = 0; j < zero_count; ++j) {
|
|
shader_code_.push_back(0);
|
|
}
|
|
++stat_.instruction_count;
|
|
++stat_.mov_instruction_count;
|
|
}
|
|
}
|
|
|
|
return register_index;
|
|
}
|
|
|
|
void DxbcShaderTranslator::PopSystemTemp(uint32_t count) {
|
|
assert_true(count <= system_temp_count_current_);
|
|
system_temp_count_current_ -= std::min(count, system_temp_count_current_);
|
|
}
|
|
|
|
void DxbcShaderTranslator::ConvertPWLGamma(
|
|
bool to_gamma, int32_t source_temp, uint32_t source_temp_component,
|
|
uint32_t target_temp, uint32_t target_temp_component, uint32_t piece_temp,
|
|
uint32_t piece_temp_component, uint32_t accumulator_temp,
|
|
uint32_t accumulator_temp_component) {
|
|
assert_true(source_temp != target_temp ||
|
|
source_temp_component != target_temp_component ||
|
|
((target_temp != accumulator_temp ||
|
|
target_temp_component != accumulator_temp_component) &&
|
|
(target_temp != piece_temp ||
|
|
target_temp_component != piece_temp_component)));
|
|
assert_true(piece_temp != source_temp ||
|
|
piece_temp_component != source_temp_component);
|
|
assert_true(accumulator_temp != source_temp ||
|
|
accumulator_temp_component != source_temp_component);
|
|
assert_true(piece_temp != accumulator_temp ||
|
|
piece_temp_component != accumulator_temp_component);
|
|
uint32_t piece_temp_mask = 1 << piece_temp_component;
|
|
uint32_t accumulator_temp_mask = 1 << accumulator_temp_component;
|
|
|
|
// For each piece:
|
|
// 1) Calculate how far we are on it. Multiply by 1/width, subtract
|
|
// start/width and saturate.
|
|
// 2) Add the contribution of the piece - multiply the position on the piece
|
|
// by its slope*width and accumulate.
|
|
|
|
// Piece 1.
|
|
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MUL) |
|
|
ENCODE_D3D10_SB_INSTRUCTION_SATURATE(1) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7));
|
|
shader_code_.push_back(EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP,
|
|
piece_temp_mask, 1));
|
|
shader_code_.push_back(piece_temp);
|
|
shader_code_.push_back(EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP,
|
|
source_temp_component, 1));
|
|
shader_code_.push_back(source_temp);
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
// 1.0 / 0.0625 to, 1.0 / 0.25 from.
|
|
shader_code_.push_back(to_gamma ? 0x41800000u : 0x40800000u);
|
|
++stat_.instruction_count;
|
|
++stat_.float_instruction_count;
|
|
|
|
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,
|
|
accumulator_temp_mask, 1));
|
|
shader_code_.push_back(accumulator_temp);
|
|
shader_code_.push_back(EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP,
|
|
piece_temp_component, 1));
|
|
shader_code_.push_back(piece_temp);
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
// 4.0 * 0.0625 to, 0.25 * 0.25 from.
|
|
shader_code_.push_back(to_gamma ? 0x3E800000u : 0x3D800000u);
|
|
++stat_.instruction_count;
|
|
++stat_.float_instruction_count;
|
|
|
|
// Piece 2.
|
|
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAD) |
|
|
ENCODE_D3D10_SB_INSTRUCTION_SATURATE(1) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9));
|
|
shader_code_.push_back(EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP,
|
|
piece_temp_mask, 1));
|
|
shader_code_.push_back(piece_temp);
|
|
shader_code_.push_back(EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP,
|
|
source_temp_component, 1));
|
|
shader_code_.push_back(source_temp);
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
// 1.0 / 0.0625 to, 1.0 / 0.125 from.
|
|
shader_code_.push_back(to_gamma ? 0x41800000u : 0x41000000u);
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
// -0.0625 / 0.0625 to, -0.25 / 0.125 from.
|
|
shader_code_.push_back(to_gamma ? 0xBF800000u : 0xC0000000u);
|
|
++stat_.instruction_count;
|
|
++stat_.float_instruction_count;
|
|
|
|
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,
|
|
accumulator_temp_mask, 1));
|
|
shader_code_.push_back(accumulator_temp);
|
|
shader_code_.push_back(EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP,
|
|
piece_temp_component, 1));
|
|
shader_code_.push_back(piece_temp);
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
// 2.0 * 0.0625 to, 0.5 * 0.125 from.
|
|
shader_code_.push_back(to_gamma ? 0x3E000000u : 0x3D800000u);
|
|
shader_code_.push_back(EncodeVectorSelectOperand(
|
|
D3D10_SB_OPERAND_TYPE_TEMP, accumulator_temp_component, 1));
|
|
shader_code_.push_back(accumulator_temp);
|
|
++stat_.instruction_count;
|
|
++stat_.float_instruction_count;
|
|
|
|
// Piece 3.
|
|
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAD) |
|
|
ENCODE_D3D10_SB_INSTRUCTION_SATURATE(1) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9));
|
|
shader_code_.push_back(EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP,
|
|
piece_temp_mask, 1));
|
|
shader_code_.push_back(piece_temp);
|
|
shader_code_.push_back(EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP,
|
|
source_temp_component, 1));
|
|
shader_code_.push_back(source_temp);
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
// 1.0 / 0.375 to, 1.0 / 0.375 from.
|
|
shader_code_.push_back(0x402AAAABu);
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
// -0.125 / 0.375 to, -0.375 / 0.375 from.
|
|
shader_code_.push_back(to_gamma ? 0xBEAAAAABu : 0xBF800000u);
|
|
++stat_.instruction_count;
|
|
++stat_.float_instruction_count;
|
|
|
|
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,
|
|
accumulator_temp_mask, 1));
|
|
shader_code_.push_back(accumulator_temp);
|
|
shader_code_.push_back(EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP,
|
|
piece_temp_component, 1));
|
|
shader_code_.push_back(piece_temp);
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
// 1.0 * 0.375 to, 1.0 * 0.375 from.
|
|
shader_code_.push_back(0x3EC00000u);
|
|
shader_code_.push_back(EncodeVectorSelectOperand(
|
|
D3D10_SB_OPERAND_TYPE_TEMP, accumulator_temp_component, 1));
|
|
shader_code_.push_back(accumulator_temp);
|
|
++stat_.instruction_count;
|
|
++stat_.float_instruction_count;
|
|
|
|
// Piece 4.
|
|
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAD) |
|
|
ENCODE_D3D10_SB_INSTRUCTION_SATURATE(1) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9));
|
|
shader_code_.push_back(EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP,
|
|
piece_temp_mask, 1));
|
|
shader_code_.push_back(piece_temp);
|
|
shader_code_.push_back(EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP,
|
|
source_temp_component, 1));
|
|
shader_code_.push_back(source_temp);
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
// 1.0 / 0.5 to, 1.0 / 0.25 from.
|
|
shader_code_.push_back(to_gamma ? 0x40000000u : 0x40800000u);
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
// -0.5 / 0.5 to, -0.75 / 0.25 from.
|
|
shader_code_.push_back(to_gamma ? 0xBF800000u : 0xC0400000u);
|
|
++stat_.instruction_count;
|
|
++stat_.float_instruction_count;
|
|
|
|
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, 1 << target_temp_component, 1));
|
|
shader_code_.push_back(target_temp);
|
|
shader_code_.push_back(EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP,
|
|
piece_temp_component, 1));
|
|
shader_code_.push_back(piece_temp);
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
// 0.5 * 0.5 to, 2.0 * 0.25 from.
|
|
shader_code_.push_back(to_gamma ? 0x3E800000u : 0x3F000000u);
|
|
shader_code_.push_back(EncodeVectorSelectOperand(
|
|
D3D10_SB_OPERAND_TYPE_TEMP, accumulator_temp_component, 1));
|
|
shader_code_.push_back(accumulator_temp);
|
|
++stat_.instruction_count;
|
|
++stat_.float_instruction_count;
|
|
}
|
|
|
|
void DxbcShaderTranslator::StartVertexShader_LoadVertexIndex() {
|
|
if (register_count() < 1) {
|
|
return;
|
|
}
|
|
|
|
// Vertex index is in an input bound to SV_VertexID, byte swapped according to
|
|
// xe_vertex_index_endian_and_edge_factors system constant and written to
|
|
// GPR 0.
|
|
|
|
// xe_vertex_index_endian_and_edge_factors & 0b11 is:
|
|
// - 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.
|
|
|
|
// Write to GPR 0 - either directly if not using indexable registers, or via a
|
|
// system temporary register.
|
|
uint32_t reg;
|
|
if (uses_register_dynamic_addressing()) {
|
|
reg = PushSystemTemp();
|
|
} else {
|
|
reg = 0;
|
|
}
|
|
|
|
// Check if the closing vertex of a non-indexed line loop is being processed.
|
|
system_constants_used_ |= 1ull << kSysConst_LineLoopClosingIndex_Index;
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_INE) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9));
|
|
shader_code_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1));
|
|
shader_code_.push_back(reg);
|
|
shader_code_.push_back(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_INPUT, 0, 1));
|
|
shader_code_.push_back(uint32_t(InOutRegister::kVSInVertexIndex));
|
|
shader_code_.push_back(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER,
|
|
kSysConst_LineLoopClosingIndex_Comp, 3));
|
|
shader_code_.push_back(cbuffer_index_system_constants_);
|
|
shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants));
|
|
shader_code_.push_back(kSysConst_LineLoopClosingIndex_Vec);
|
|
++stat_.instruction_count;
|
|
++stat_.int_instruction_count;
|
|
// Zero the index if processing the closing vertex of a line loop, or do
|
|
// nothing (replace 0 with 0) if not needed.
|
|
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(reg);
|
|
shader_code_.push_back(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_INPUT, 0, 1));
|
|
shader_code_.push_back(uint32_t(InOutRegister::kVSInVertexIndex));
|
|
shader_code_.push_back(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1));
|
|
shader_code_.push_back(reg);
|
|
++stat_.instruction_count;
|
|
++stat_.uint_instruction_count;
|
|
|
|
// 8-in-16: Create target for A and C insertion in Y and sources in X and Z.
|
|
// ushr reg.xyz, input, l(0, 8, 16, 0)
|
|
// ABCD | BCD0 | CD00 | unused
|
|
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, 0b0111, 1));
|
|
shader_code_.push_back(reg);
|
|
shader_code_.push_back(
|
|
EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1));
|
|
shader_code_.push_back(reg);
|
|
shader_code_.push_back(EncodeVectorSwizzledOperand(
|
|
D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0));
|
|
shader_code_.push_back(0);
|
|
shader_code_.push_back(8);
|
|
shader_code_.push_back(16);
|
|
shader_code_.push_back(0);
|
|
++stat_.instruction_count;
|
|
++stat_.uint_instruction_count;
|
|
|
|
// 8-in-16: Insert A in Y.
|
|
// bfi reg.y, l(8), l(8), reg.x, reg.y
|
|
// ABCD | BAD0 | CD00 | unused
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_BFI) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(11));
|
|
shader_code_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1));
|
|
shader_code_.push_back(reg);
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
shader_code_.push_back(8);
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
shader_code_.push_back(8);
|
|
shader_code_.push_back(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1));
|
|
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_.uint_instruction_count;
|
|
|
|
// 8-in-16: Insert C in W.
|
|
// bfi reg.y, l(8), l(24), reg.z, reg.y
|
|
// ABCD | BADC | CD00 | unused
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_BFI) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(11));
|
|
shader_code_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1));
|
|
shader_code_.push_back(reg);
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
shader_code_.push_back(8);
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
shader_code_.push_back(24);
|
|
shader_code_.push_back(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1));
|
|
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_.uint_instruction_count;
|
|
|
|
// Get bits indicating what swaps should be done.
|
|
// ubfe reg.zw, l(0, 0, 1, 1).zw, l(0, 0, 0, 1).zw,
|
|
// xe_vertex_index_endian_and_edge_factors.xx
|
|
// ABCD | BADC | 8in16/16in32? | 8in32/16in32?
|
|
system_constants_used_ |= 1ull
|
|
<< kSysConst_VertexIndexEndianAndEdgeFactors_Index;
|
|
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, 0b1100, 1));
|
|
shader_code_.push_back(reg);
|
|
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(1);
|
|
shader_code_.push_back(1);
|
|
shader_code_.push_back(EncodeVectorSwizzledOperand(
|
|
D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0));
|
|
shader_code_.push_back(0);
|
|
shader_code_.push_back(0);
|
|
shader_code_.push_back(0);
|
|
shader_code_.push_back(1);
|
|
shader_code_.push_back(EncodeVectorReplicatedOperand(
|
|
D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER,
|
|
kSysConst_VertexIndexEndianAndEdgeFactors_Comp, 3));
|
|
shader_code_.push_back(uint32_t(cbuffer_index_system_constants_));
|
|
shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants));
|
|
shader_code_.push_back(kSysConst_VertexIndexEndianAndEdgeFactors_Vec);
|
|
++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 reg.z, reg.z, reg.w
|
|
// ABCD | BADC | 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, 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(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1));
|
|
shader_code_.push_back(reg);
|
|
++stat_.instruction_count;
|
|
++stat_.uint_instruction_count;
|
|
|
|
// Write the 8-in-16 value to X if needed.
|
|
// movc reg.x, reg.z, reg.y, reg.x
|
|
// ABCD/BADC | unused | unused | 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, 0b0001, 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(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1));
|
|
shader_code_.push_back(reg);
|
|
shader_code_.push_back(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1));
|
|
shader_code_.push_back(reg);
|
|
++stat_.instruction_count;
|
|
++stat_.movc_instruction_count;
|
|
|
|
// 16-in-32: Write the low 16 bits.
|
|
// ushr reg.y, reg.x, l(16)
|
|
// ABCD/BADC | CD00/DC00 | unused | 8in32/16in32?
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_USHR) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7));
|
|
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, 0, 1));
|
|
shader_code_.push_back(reg);
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
shader_code_.push_back(16);
|
|
++stat_.instruction_count;
|
|
++stat_.uint_instruction_count;
|
|
|
|
// 16-in-32: Write the high 16 bits.
|
|
// bfi reg.y, l(16), l(16), reg.x, reg.y
|
|
// ABCD/BADC | CDAB/DCBA | unused | 8in32/16in32?
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_BFI) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(11));
|
|
shader_code_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0010, 1));
|
|
shader_code_.push_back(reg);
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
shader_code_.push_back(16);
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
shader_code_.push_back(16);
|
|
shader_code_.push_back(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1));
|
|
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_.uint_instruction_count;
|
|
|
|
// Apply the 16-in-32 swap if needed.
|
|
// movc reg.x, reg.w, reg.y, reg.x
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9));
|
|
shader_code_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1));
|
|
shader_code_.push_back(reg);
|
|
shader_code_.push_back(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1));
|
|
shader_code_.push_back(reg);
|
|
shader_code_.push_back(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1));
|
|
shader_code_.push_back(reg);
|
|
shader_code_.push_back(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1));
|
|
shader_code_.push_back(reg);
|
|
++stat_.instruction_count;
|
|
++stat_.movc_instruction_count;
|
|
|
|
// Add the base vertex index.
|
|
system_constants_used_ |= 1ull << kSysConst_VertexBaseIndex_Index;
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IADD) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9));
|
|
shader_code_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1));
|
|
shader_code_.push_back(reg);
|
|
shader_code_.push_back(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1));
|
|
shader_code_.push_back(reg);
|
|
shader_code_.push_back(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER,
|
|
kSysConst_VertexBaseIndex_Comp, 3));
|
|
shader_code_.push_back(uint32_t(cbuffer_index_system_constants_));
|
|
shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants));
|
|
shader_code_.push_back(kSysConst_VertexBaseIndex_Vec);
|
|
++stat_.instruction_count;
|
|
++stat_.int_instruction_count;
|
|
|
|
// Convert to float and replicate the swapped value in the destination
|
|
// register (what should be in YZW is unknown, but just to make it a bit
|
|
// cleaner).
|
|
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, 0b1111, 1));
|
|
shader_code_.push_back(reg);
|
|
shader_code_.push_back(
|
|
EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1));
|
|
shader_code_.push_back(reg);
|
|
++stat_.instruction_count;
|
|
++stat_.conversion_instruction_count;
|
|
|
|
if (uses_register_dynamic_addressing()) {
|
|
// Store to indexed GPR 0 in x0[0].
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(6));
|
|
shader_code_.push_back(EncodeVectorMaskedOperand(
|
|
D3D10_SB_OPERAND_TYPE_INDEXABLE_TEMP, 0b1111, 2));
|
|
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(reg);
|
|
++stat_.instruction_count;
|
|
++stat_.array_instruction_count;
|
|
PopSystemTemp();
|
|
}
|
|
}
|
|
|
|
void DxbcShaderTranslator::StartVertexOrDomainShader() {
|
|
// Zero the interpolators.
|
|
for (uint32_t i = 0; i < kInterpolatorCount; ++i) {
|
|
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_OUTPUT, 0b1111, 1));
|
|
shader_code_.push_back(uint32_t(InOutRegister::kVSOutInterpolators) + i);
|
|
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;
|
|
}
|
|
|
|
// Zero the point coordinate (will be set in the geometry shader if needed)
|
|
// and set the point size to a negative value to tell the geometry shader that
|
|
// it should use the global point size - the vertex shader may overwrite it
|
|
// later.
|
|
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_OUTPUT, 0b0111, 1));
|
|
shader_code_.push_back(uint32_t(InOutRegister::kVSOutPointParameters));
|
|
shader_code_.push_back(EncodeVectorSwizzledOperand(
|
|
D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0));
|
|
shader_code_.push_back(0);
|
|
shader_code_.push_back(0);
|
|
// -1.0f
|
|
shader_code_.push_back(0xBF800000u);
|
|
shader_code_.push_back(0);
|
|
++stat_.instruction_count;
|
|
++stat_.mov_instruction_count;
|
|
|
|
if (IsDxbcVertexShader()) {
|
|
// Write the vertex index to GPR 0.
|
|
StartVertexShader_LoadVertexIndex();
|
|
} else if (IsDxbcDomainShader()) {
|
|
assert_true(register_count() >= 2);
|
|
if (register_count() != 0) {
|
|
uint32_t temp_register_operand_length =
|
|
uses_register_dynamic_addressing() ? 3 : 2;
|
|
|
|
// Copy the domain location to r0.yz (for quad patches) or r0.xyz (for
|
|
// triangle patches), and also set the domain in STAT.
|
|
uint32_t domain_location_mask, domain_location_swizzle;
|
|
if (patch_primitive_type() == PrimitiveType::kTrianglePatch) {
|
|
domain_location_mask = 0b0111;
|
|
// ZYX swizzle with r1.y == 0, according to the water shader in
|
|
// Banjo-Kazooie: Nuts & Bolts.
|
|
domain_location_swizzle = 0b00000110;
|
|
stat_.tessellator_domain = D3D11_SB_TESSELLATOR_DOMAIN_TRI;
|
|
} else {
|
|
// TODO(Triang3l): Support line patches.
|
|
assert_true(patch_primitive_type() == PrimitiveType::kQuadPatch);
|
|
// According to the ground shader in Viva Pinata, though it's impossible
|
|
// (as of December 12th, 2018) to test there since it possibly requires
|
|
// memexport for ground control points (the memory region with them is
|
|
// filled with zeros).
|
|
domain_location_mask = 0b0110;
|
|
domain_location_swizzle = 0b00000100;
|
|
stat_.tessellator_domain = D3D11_SB_TESSELLATOR_DOMAIN_QUAD;
|
|
}
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(
|
|
2 + temp_register_operand_length));
|
|
if (uses_register_dynamic_addressing()) {
|
|
shader_code_.push_back(EncodeVectorMaskedOperand(
|
|
D3D10_SB_OPERAND_TYPE_INDEXABLE_TEMP, domain_location_mask, 2));
|
|
shader_code_.push_back(0);
|
|
} else {
|
|
shader_code_.push_back(EncodeVectorMaskedOperand(
|
|
D3D10_SB_OPERAND_TYPE_TEMP, domain_location_mask, 1));
|
|
}
|
|
shader_code_.push_back(0);
|
|
shader_code_.push_back(
|
|
EncodeVectorSwizzledOperand(D3D11_SB_OPERAND_TYPE_INPUT_DOMAIN_POINT,
|
|
domain_location_swizzle, 0));
|
|
++stat_.instruction_count;
|
|
if (uses_register_dynamic_addressing()) {
|
|
++stat_.array_instruction_count;
|
|
} else {
|
|
++stat_.mov_instruction_count;
|
|
}
|
|
|
|
// Copy the primitive index to r0.x (for quad patches) or r1.x (for
|
|
// triangle patches) as a float.
|
|
// When using indexable temps, copy through a r# because x# are apparently
|
|
// only accessible via mov.
|
|
// TODO(Triang3l): Investigate what should be written for primitives (or
|
|
// even control points) for non-adaptive tessellation modes (they may
|
|
// possibly have an index buffer).
|
|
// TODO(Triang3l): Support line patches.
|
|
uint32_t primitive_id_gpr_index =
|
|
patch_primitive_type() == PrimitiveType::kTrianglePatch ? 1 : 0;
|
|
|
|
if (register_count() > primitive_id_gpr_index) {
|
|
uint32_t primitive_id_temp = uses_register_dynamic_addressing()
|
|
? PushSystemTemp()
|
|
: primitive_id_gpr_index;
|
|
shader_code_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_UTOF) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(4));
|
|
shader_code_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1));
|
|
shader_code_.push_back(primitive_id_temp);
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_INPUT_PRIMITIVEID, 0));
|
|
++stat_.instruction_count;
|
|
++stat_.conversion_instruction_count;
|
|
if (uses_register_dynamic_addressing()) {
|
|
shader_code_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(6));
|
|
shader_code_.push_back(EncodeVectorMaskedOperand(
|
|
D3D10_SB_OPERAND_TYPE_INDEXABLE_TEMP, 0b0001, 2));
|
|
shader_code_.push_back(0);
|
|
shader_code_.push_back(primitive_id_gpr_index);
|
|
shader_code_.push_back(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1));
|
|
shader_code_.push_back(primitive_id_temp);
|
|
++stat_.instruction_count;
|
|
++stat_.array_instruction_count;
|
|
// Release primitive_id_temp.
|
|
PopSystemTemp();
|
|
}
|
|
}
|
|
|
|
if (register_count() >= 2) {
|
|
// Write the swizzle of the barycentric/UV coordinates to r1.x (for quad
|
|
// patches) or r1.y (for triangle patches). It appears that the
|
|
// tessellator offloads the reordering of coordinates for edges to game
|
|
// shaders.
|
|
//
|
|
// In Banjo-Kazooie: Nuts & Bolts (triangle patches with per-edge
|
|
// factors), the shader multiplies the first control point's position by
|
|
// r0.z, the second CP's by r0.y, and the third CP's by r0.x. But before
|
|
// doing that it swizzles r0.xyz the following way depending on the
|
|
// value in r1.y:
|
|
// - ZXY for 1.0.
|
|
// - YZX for 2.0.
|
|
// - XZY for 4.0.
|
|
// - YXZ for 5.0.
|
|
// - ZYX for 6.0.
|
|
// Possibly, the logic here is that the value itself is the amount of
|
|
// rotation of the swizzle to the right, and 1 << 2 is set when the
|
|
// swizzle needs to be flipped before rotating.
|
|
//
|
|
// In Viva Pinata (quad patches with per-edge factors - not possible to
|
|
// test however as of December 12th, 2018), if we assume that r0.y is V
|
|
// and r0.z is U, the factors each control point value is multiplied by
|
|
// are the following:
|
|
// - (1-v)*(1-u), v*(1-u), (1-v)*u, v*u for 0.0 (base swizzle).
|
|
// - v*(1-u), (1-v)*(1-u), v*u, (1-v)*u for 1.0 (YXWZ).
|
|
// - v*u, (1-v)*u, v*(1-u), (1-v)*(1-u) for 2.0 (WZYX).
|
|
// - (1-v)*u, v*u, (1-v)*(1-u), v*(1-u) for 3.0 (ZWXY).
|
|
// According to the control point order at
|
|
// https://www.khronos.org/registry/OpenGL/extensions/AMD/AMD_vertex_shader_tessellator.txt
|
|
// the first is located at (0,0), the second at (0,1), the third at
|
|
// (1,0) and the fourth at (1,1). So, swizzle index 0 appears to be the
|
|
// correct one. But, this hasn't been tested yet.
|
|
//
|
|
// Direct3D 12 appears to be passing the coordinates in a consistent
|
|
// order, so we can just use ZYX for triangle patches.
|
|
//
|
|
// TODO(Triang3l): Support line patches.
|
|
uint32_t domain_location_swizzle_mask =
|
|
patch_primitive_type() == PrimitiveType::kTrianglePatch ? 0b0010
|
|
: 0b0001;
|
|
shader_code_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(
|
|
3 + temp_register_operand_length));
|
|
if (uses_register_dynamic_addressing()) {
|
|
shader_code_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_INDEXABLE_TEMP,
|
|
domain_location_swizzle_mask, 2));
|
|
shader_code_.push_back(0);
|
|
} else {
|
|
shader_code_.push_back(EncodeVectorMaskedOperand(
|
|
D3D10_SB_OPERAND_TYPE_TEMP, domain_location_swizzle_mask, 1));
|
|
}
|
|
shader_code_.push_back(1);
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
shader_code_.push_back(0);
|
|
++stat_.instruction_count;
|
|
if (uses_register_dynamic_addressing()) {
|
|
++stat_.array_instruction_count;
|
|
} else {
|
|
++stat_.mov_instruction_count;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void DxbcShaderTranslator::StartPixelShader() {
|
|
if (edram_rov_used_) {
|
|
// Load the EDRAM addresses and the coverage.
|
|
StartPixelShader_LoadROVParameters();
|
|
|
|
// Do early 2x2 quad rejection if it makes sense.
|
|
if (ROV_IsDepthStencilEarly()) {
|
|
ROV_DepthStencilTest();
|
|
}
|
|
}
|
|
|
|
// If not translating anything, we only need the depth.
|
|
if (is_depth_only_pixel_shader_) {
|
|
return;
|
|
}
|
|
|
|
// Copy interpolants to GPRs.
|
|
uint32_t interpolator_count = std::min(kInterpolatorCount, register_count());
|
|
if (uses_register_dynamic_addressing()) {
|
|
for (uint32_t i = 0; i < interpolator_count; ++i) {
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(6));
|
|
shader_code_.push_back(EncodeVectorMaskedOperand(
|
|
D3D10_SB_OPERAND_TYPE_INDEXABLE_TEMP, 0b1111, 2));
|
|
shader_code_.push_back(0);
|
|
shader_code_.push_back(i);
|
|
shader_code_.push_back(EncodeVectorSwizzledOperand(
|
|
D3D10_SB_OPERAND_TYPE_INPUT, kSwizzleXYZW, 1));
|
|
shader_code_.push_back(uint32_t(InOutRegister::kPSInInterpolators) + i);
|
|
++stat_.instruction_count;
|
|
++stat_.array_instruction_count;
|
|
}
|
|
} else {
|
|
for (uint32_t i = 0; i < interpolator_count; ++i) {
|
|
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, 0b1111, 1));
|
|
shader_code_.push_back(i);
|
|
shader_code_.push_back(EncodeVectorSwizzledOperand(
|
|
D3D10_SB_OPERAND_TYPE_INPUT, kSwizzleXYZW, 1));
|
|
shader_code_.push_back(uint32_t(InOutRegister::kPSInInterpolators) + i);
|
|
++stat_.instruction_count;
|
|
++stat_.mov_instruction_count;
|
|
}
|
|
}
|
|
|
|
// Write screen and point coordinates to the specified interpolator register
|
|
// (ps_param_gen).
|
|
uint32_t param_gen_select_temp = PushSystemTemp();
|
|
uint32_t param_gen_value_temp = PushSystemTemp();
|
|
// Check if they need to be written.
|
|
system_constants_used_ |= 1ull << kSysConst_PixelPosReg_Index;
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ULT) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9));
|
|
shader_code_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1));
|
|
shader_code_.push_back(param_gen_select_temp);
|
|
shader_code_.push_back(EncodeVectorSelectOperand(
|
|
D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSysConst_PixelPosReg_Comp, 3));
|
|
shader_code_.push_back(cbuffer_index_system_constants_);
|
|
shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants));
|
|
shader_code_.push_back(kSysConst_PixelPosReg_Vec);
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
shader_code_.push_back(interpolator_count);
|
|
++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, 0, 1));
|
|
shader_code_.push_back(param_gen_select_temp);
|
|
++stat_.instruction_count;
|
|
++stat_.dynamic_flow_control_count;
|
|
// Write VPOS (without supersampling because SSAA is used to fake MSAA with
|
|
// RTV/DSV), at integer coordinates rather than half-pixel if needed, to XY.
|
|
if (!edram_rov_used_) {
|
|
// Get inverse of the sample size.
|
|
system_constants_used_ |= 1ull << kSysConst_SampleCountLog2_Index;
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IMAD) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(17));
|
|
shader_code_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0011, 1));
|
|
shader_code_.push_back(param_gen_value_temp);
|
|
shader_code_.push_back(EncodeVectorSwizzledOperand(
|
|
D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER,
|
|
kSysConst_SampleCountLog2_Comp |
|
|
((kSysConst_SampleCountLog2_Comp + 1) << 2),
|
|
3));
|
|
shader_code_.push_back(cbuffer_index_system_constants_);
|
|
shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants));
|
|
shader_code_.push_back(kSysConst_SampleCountLog2_Vec);
|
|
shader_code_.push_back(EncodeVectorSwizzledOperand(
|
|
D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0));
|
|
shader_code_.push_back(uint32_t(-(1 << 23)));
|
|
shader_code_.push_back(uint32_t(-(1 << 23)));
|
|
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(0x3F800000);
|
|
shader_code_.push_back(0x3F800000);
|
|
shader_code_.push_back(0);
|
|
shader_code_.push_back(0);
|
|
++stat_.instruction_count;
|
|
++stat_.int_instruction_count;
|
|
// Convert from samples to pixels.
|
|
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, 0b0011, 1));
|
|
shader_code_.push_back(param_gen_value_temp);
|
|
shader_code_.push_back(EncodeVectorSwizzledOperand(
|
|
D3D10_SB_OPERAND_TYPE_INPUT, kSwizzleXYZW, 1));
|
|
shader_code_.push_back(uint32_t(InOutRegister::kPSInPosition));
|
|
shader_code_.push_back(EncodeVectorSwizzledOperand(
|
|
D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1));
|
|
shader_code_.push_back(param_gen_value_temp);
|
|
++stat_.instruction_count;
|
|
++stat_.float_instruction_count;
|
|
}
|
|
// Floor VPOS so with SSAA, the resulting pixel corner/center is written, not
|
|
// the sample position.
|
|
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, 0b0011, 1));
|
|
shader_code_.push_back(param_gen_value_temp);
|
|
shader_code_.push_back(
|
|
EncodeVectorSwizzledOperand(edram_rov_used_ ? D3D10_SB_OPERAND_TYPE_INPUT
|
|
: D3D10_SB_OPERAND_TYPE_TEMP,
|
|
kSwizzleXYZW, 1));
|
|
shader_code_.push_back(edram_rov_used_
|
|
? uint32_t(InOutRegister::kPSInPosition)
|
|
: param_gen_value_temp);
|
|
++stat_.instruction_count;
|
|
++stat_.float_instruction_count;
|
|
// If in OpenGL half-pixel offset mode, write the center of the pixel.
|
|
system_constants_used_ |= 1ull << kSysConst_PixelHalfPixelOffset_Index;
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ADD) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9));
|
|
shader_code_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0011, 1));
|
|
shader_code_.push_back(param_gen_value_temp);
|
|
shader_code_.push_back(
|
|
EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1));
|
|
shader_code_.push_back(param_gen_value_temp);
|
|
shader_code_.push_back(
|
|
EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER,
|
|
kSysConst_PixelHalfPixelOffset_Comp, 3));
|
|
shader_code_.push_back(cbuffer_index_system_constants_);
|
|
shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants));
|
|
shader_code_.push_back(kSysConst_PixelHalfPixelOffset_Vec);
|
|
++stat_.instruction_count;
|
|
++stat_.float_instruction_count;
|
|
// Undo 2x resolution scale in VPOS.
|
|
if (edram_rov_used_) {
|
|
// Check if resolution scale is 4.
|
|
system_constants_used_ |= 1ull
|
|
<< kSysConst_EDRAMResolutionSquareScale_Index;
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IEQ) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9));
|
|
shader_code_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1));
|
|
shader_code_.push_back(param_gen_value_temp);
|
|
shader_code_.push_back(EncodeVectorSelectOperand(
|
|
D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER,
|
|
kSysConst_EDRAMResolutionSquareScale_Comp, 3));
|
|
shader_code_.push_back(cbuffer_index_system_constants_);
|
|
shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants));
|
|
shader_code_.push_back(kSysConst_EDRAMResolutionSquareScale_Vec);
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
shader_code_.push_back(4);
|
|
++stat_.instruction_count;
|
|
++stat_.int_instruction_count;
|
|
// Get inverse of the width/height scale.
|
|
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(param_gen_value_temp);
|
|
shader_code_.push_back(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1));
|
|
shader_code_.push_back(param_gen_value_temp);
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
// 0.5
|
|
shader_code_.push_back(0x3F000000);
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
// 1.0
|
|
shader_code_.push_back(0x3F800000);
|
|
++stat_.instruction_count;
|
|
++stat_.int_instruction_count;
|
|
// Convert to guest pixels.
|
|
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, 0b0011, 1));
|
|
shader_code_.push_back(param_gen_value_temp);
|
|
shader_code_.push_back(EncodeVectorSwizzledOperand(
|
|
D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1));
|
|
shader_code_.push_back(param_gen_value_temp);
|
|
shader_code_.push_back(
|
|
EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1));
|
|
shader_code_.push_back(param_gen_value_temp);
|
|
++stat_.instruction_count;
|
|
++stat_.float_instruction_count;
|
|
}
|
|
// Write point sprite coordinates to ZW.
|
|
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, 0b1100, 1));
|
|
shader_code_.push_back(param_gen_value_temp);
|
|
shader_code_.push_back(
|
|
EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_INPUT, 0b01000000, 1));
|
|
shader_code_.push_back(uint32_t(InOutRegister::kPSInPointParameters));
|
|
++stat_.instruction_count;
|
|
++stat_.mov_instruction_count;
|
|
if (uses_register_dynamic_addressing()) {
|
|
// Copy the register index to an r# so it can be used for indexable temp
|
|
// addressing.
|
|
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, 0b0001, 1));
|
|
shader_code_.push_back(param_gen_select_temp);
|
|
shader_code_.push_back(EncodeVectorSelectOperand(
|
|
D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSysConst_PixelPosReg_Comp, 3));
|
|
shader_code_.push_back(cbuffer_index_system_constants_);
|
|
shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants));
|
|
shader_code_.push_back(kSysConst_PixelPosReg_Vec);
|
|
++stat_.instruction_count;
|
|
++stat_.mov_instruction_count;
|
|
// Store the value to an x0[xe_pixel_pos_reg].
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7));
|
|
shader_code_.push_back(EncodeVectorMaskedOperand(
|
|
D3D10_SB_OPERAND_TYPE_INDEXABLE_TEMP, 0b1111, 2,
|
|
D3D10_SB_OPERAND_INDEX_IMMEDIATE32, D3D10_SB_OPERAND_INDEX_RELATIVE));
|
|
shader_code_.push_back(0);
|
|
shader_code_.push_back(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1));
|
|
shader_code_.push_back(param_gen_select_temp);
|
|
shader_code_.push_back(EncodeVectorSwizzledOperand(
|
|
D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1));
|
|
shader_code_.push_back(param_gen_value_temp);
|
|
++stat_.instruction_count;
|
|
++stat_.array_instruction_count;
|
|
} else {
|
|
// Store to the needed register using movc.
|
|
for (uint32_t i = 0; i < interpolator_count; ++i) {
|
|
if ((i & 3) == 0) {
|
|
// Get a mask of whether the current register index is the target one.
|
|
shader_code_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IEQ) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(12));
|
|
shader_code_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1));
|
|
shader_code_.push_back(param_gen_select_temp);
|
|
shader_code_.push_back(
|
|
EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER,
|
|
kSysConst_PixelPosReg_Comp, 3));
|
|
shader_code_.push_back(cbuffer_index_system_constants_);
|
|
shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants));
|
|
shader_code_.push_back(kSysConst_PixelPosReg_Vec);
|
|
shader_code_.push_back(EncodeVectorSwizzledOperand(
|
|
D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0));
|
|
shader_code_.push_back(i);
|
|
shader_code_.push_back(i + 1);
|
|
shader_code_.push_back(i + 2);
|
|
shader_code_.push_back(i + 3);
|
|
++stat_.instruction_count;
|
|
++stat_.int_instruction_count;
|
|
}
|
|
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, 0b1111, 1));
|
|
shader_code_.push_back(i);
|
|
shader_code_.push_back(
|
|
EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, i & 3, 1));
|
|
shader_code_.push_back(param_gen_select_temp);
|
|
shader_code_.push_back(EncodeVectorSwizzledOperand(
|
|
D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1));
|
|
shader_code_.push_back(param_gen_value_temp);
|
|
shader_code_.push_back(EncodeVectorSwizzledOperand(
|
|
D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1));
|
|
shader_code_.push_back(i);
|
|
++stat_.instruction_count;
|
|
++stat_.movc_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;
|
|
// Release param_gen_select_temp and param_gen_value_temp.
|
|
PopSystemTemp(2);
|
|
}
|
|
|
|
void DxbcShaderTranslator::StartTranslation() {
|
|
// Allocate labels and registers for subroutines.
|
|
label_rov_depth_to_24bit_ = UINT32_MAX;
|
|
label_rov_depth_stencil_sample_ = UINT32_MAX;
|
|
std::memset(label_rov_color_sample_, 0xFF, sizeof(label_rov_color_sample_));
|
|
uint32_t label_index = 0;
|
|
system_temps_subroutine_count_ = 0;
|
|
if (IsDxbcPixelShader() && edram_rov_used_) {
|
|
label_rov_depth_to_24bit_ = label_index++;
|
|
system_temps_subroutine_count_ =
|
|
std::max((uint32_t)1, system_temps_subroutine_count_);
|
|
label_rov_depth_stencil_sample_ = label_index++;
|
|
system_temps_subroutine_count_ =
|
|
std::max((uint32_t)2, system_temps_subroutine_count_);
|
|
for (uint32_t i = 0; i < xe::countof(label_rov_color_sample_); ++i) {
|
|
if (writes_color_target(i)) {
|
|
label_rov_color_sample_[i] = label_index++;
|
|
system_temps_subroutine_count_ =
|
|
std::max((uint32_t)4, system_temps_subroutine_count_);
|
|
}
|
|
}
|
|
}
|
|
system_temps_subroutine_ = PushSystemTemp(0, system_temps_subroutine_count_);
|
|
|
|
// Allocate global system temporary registers that may also be used in the
|
|
// epilogue.
|
|
if (IsDxbcVertexOrDomainShader()) {
|
|
system_temp_position_ = PushSystemTemp(0b1111);
|
|
} else if (IsDxbcPixelShader()) {
|
|
if (edram_rov_used_) {
|
|
system_temp_rov_params_ = PushSystemTemp();
|
|
// If the shader doesn't write to depth, StartPixelShader will load the
|
|
// depth/stencil for early test, so no need to initialize. If it does,
|
|
// initialize it to something consistent - depth must be written on every
|
|
// shader execution path (at least in PC ps_3_0 and later shader models)
|
|
// and to make compilation easier.
|
|
system_temp_rov_depth_stencil_ =
|
|
PushSystemTemp(writes_depth() ? 0b0001 : 0);
|
|
}
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
if (writes_color_target(i)) {
|
|
system_temps_color_[i] = PushSystemTemp(0b1111);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!is_depth_only_pixel_shader_) {
|
|
// Allocate temporary registers for memexport addresses and data.
|
|
std::memset(system_temps_memexport_address_, 0xFF,
|
|
sizeof(system_temps_memexport_address_));
|
|
std::memset(system_temps_memexport_data_, 0xFF,
|
|
sizeof(system_temps_memexport_data_));
|
|
system_temp_memexport_written_ = UINT32_MAX;
|
|
const uint8_t* memexports_written = memexport_eM_written();
|
|
for (uint32_t i = 0; i < kMaxMemExports; ++i) {
|
|
uint32_t memexport_alloc_written = memexports_written[i];
|
|
if (memexport_alloc_written == 0) {
|
|
continue;
|
|
}
|
|
// If memexport is used at all, allocate a register containing whether eM#
|
|
// have actually been written to.
|
|
if (system_temp_memexport_written_ == UINT32_MAX) {
|
|
system_temp_memexport_written_ = PushSystemTemp(0b1111);
|
|
}
|
|
system_temps_memexport_address_[i] = PushSystemTemp(0b1111);
|
|
uint32_t memexport_data_index;
|
|
while (xe::bit_scan_forward(memexport_alloc_written,
|
|
&memexport_data_index)) {
|
|
memexport_alloc_written &= ~(1u << memexport_data_index);
|
|
system_temps_memexport_data_[i][memexport_data_index] =
|
|
PushSystemTemp();
|
|
}
|
|
}
|
|
|
|
// Allocate system temporary variables for the translated code.
|
|
system_temp_pv_ = PushSystemTemp();
|
|
system_temp_ps_pc_p0_a0_ = PushSystemTemp(0b1111);
|
|
system_temp_aL_ = PushSystemTemp(0b1111);
|
|
system_temp_loop_count_ = PushSystemTemp(0b1111);
|
|
system_temp_grad_h_lod_ = PushSystemTemp(0b1111);
|
|
system_temp_grad_v_ = PushSystemTemp(0b0111);
|
|
}
|
|
|
|
// Zero general-purpose registers to prevent crashes when the game references
|
|
// them.
|
|
for (uint32_t i = IsDxbcPixelShader() ? kInterpolatorCount : 0;
|
|
i < register_count(); ++i) {
|
|
DxbcOpMov(
|
|
uses_register_dynamic_addressing() ? DxbcDest::X(0, i) : DxbcDest::R(i),
|
|
DxbcSrc::LU(uint32_t(0)));
|
|
}
|
|
|
|
// Write stage-specific prologue.
|
|
if (IsDxbcVertexOrDomainShader()) {
|
|
StartVertexOrDomainShader();
|
|
} else if (IsDxbcPixelShader()) {
|
|
StartPixelShader();
|
|
}
|
|
|
|
// If not translating anything, don't start the main loop.
|
|
if (is_depth_only_pixel_shader_) {
|
|
return;
|
|
}
|
|
|
|
// Start the main loop (for jumping to labels by setting pc and continuing).
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_LOOP) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1));
|
|
++stat_.instruction_count;
|
|
++stat_.dynamic_flow_control_count;
|
|
// Switch and the first label (pc == 0).
|
|
if (UseSwitchForControlFlow()) {
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_SWITCH) |
|
|
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_ps_pc_p0_a0_);
|
|
++stat_.instruction_count;
|
|
++stat_.dynamic_flow_control_count;
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_CASE) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3));
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
shader_code_.push_back(0);
|
|
++stat_.instruction_count;
|
|
++stat_.static_flow_control_count;
|
|
} else {
|
|
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, 1, 1));
|
|
shader_code_.push_back(system_temp_ps_pc_p0_a0_);
|
|
++stat_.instruction_count;
|
|
++stat_.dynamic_flow_control_count;
|
|
}
|
|
}
|
|
|
|
void DxbcShaderTranslator::CompleteVertexOrDomainShader() {
|
|
// Get what we need to do with the position.
|
|
uint32_t ndc_control_temp = PushSystemTemp();
|
|
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(12));
|
|
shader_code_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1));
|
|
shader_code_.push_back(ndc_control_temp);
|
|
shader_code_.push_back(EncodeVectorReplicatedOperand(
|
|
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(EncodeVectorSwizzledOperand(
|
|
D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0));
|
|
shader_code_.push_back(kSysFlag_XYDividedByW);
|
|
shader_code_.push_back(kSysFlag_ZDividedByW);
|
|
shader_code_.push_back(kSysFlag_WNotReciprocal);
|
|
shader_code_.push_back(kSysFlag_ReverseZ);
|
|
++stat_.instruction_count;
|
|
++stat_.uint_instruction_count;
|
|
|
|
// Revert getting the reciprocal of W and dividing XY by W if needed.
|
|
// TODO(Triang3l): Check if having XY or Z pre-divided by W should enable
|
|
// affine interpolation.
|
|
uint32_t w_format_temp = PushSystemTemp();
|
|
// If the shader has returned 1/W, restore W. First take the reciprocal, which
|
|
// may be either W (what we need) or 1/W, depending on the vertex W format.
|
|
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, 0b0001, 1));
|
|
shader_code_.push_back(w_format_temp);
|
|
shader_code_.push_back(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1));
|
|
shader_code_.push_back(system_temp_position_);
|
|
++stat_.instruction_count;
|
|
++stat_.float_instruction_count;
|
|
// Then, if the shader returns 1/W (vtx_w0_fmt is 0), write 1/(1/W) to the
|
|
// position.
|
|
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, 0b1000, 1));
|
|
shader_code_.push_back(system_temp_position_);
|
|
shader_code_.push_back(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1));
|
|
shader_code_.push_back(ndc_control_temp);
|
|
shader_code_.push_back(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1));
|
|
shader_code_.push_back(system_temp_position_);
|
|
shader_code_.push_back(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1));
|
|
shader_code_.push_back(w_format_temp);
|
|
++stat_.instruction_count;
|
|
++stat_.movc_instruction_count;
|
|
// Multiply XYZ by W in case the shader returns XYZ/W and we'll need to
|
|
// restore XYZ.
|
|
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, 0b0111, 1));
|
|
shader_code_.push_back(w_format_temp);
|
|
shader_code_.push_back(
|
|
EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1));
|
|
shader_code_.push_back(system_temp_position_);
|
|
shader_code_.push_back(
|
|
EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1));
|
|
shader_code_.push_back(system_temp_position_);
|
|
++stat_.instruction_count;
|
|
++stat_.float_instruction_count;
|
|
// If vtx_xy_fmt and/or vtx_z_fmt are 1, XY and/or Z are pre-divided by W.
|
|
// Restore them in this case.
|
|
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(system_temp_position_);
|
|
shader_code_.push_back(
|
|
EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b00010000, 1));
|
|
shader_code_.push_back(ndc_control_temp);
|
|
shader_code_.push_back(
|
|
EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1));
|
|
shader_code_.push_back(w_format_temp);
|
|
shader_code_.push_back(
|
|
EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1));
|
|
shader_code_.push_back(system_temp_position_);
|
|
++stat_.instruction_count;
|
|
++stat_.movc_instruction_count;
|
|
// Release w_format_temp.
|
|
PopSystemTemp();
|
|
|
|
// Clip against user clip planes.
|
|
// Not possible to handle UCP_CULL_ONLY_ENA with the same shader though, since
|
|
// there can be only 8 SV_ClipDistance + SV_CullDistance values at most, but
|
|
// 12 would be needed.
|
|
uint32_t ucp_dot_temp = PushSystemTemp();
|
|
uint32_t ucp_enabled_temp = PushSystemTemp();
|
|
system_constants_used_ |= (1ull << kSysConst_UserClipPlanes_Index) |
|
|
(1ull << kSysConst_Flags_Index);
|
|
for (uint32_t i = 0; i < 2; ++i) {
|
|
uint32_t ucp_count = i ? 2 : 4;
|
|
uint32_t ucp_mask = (1 << ucp_count) - 1;
|
|
for (uint32_t j = 0; j < ucp_count; ++j) {
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DP4) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9));
|
|
shader_code_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1 << j, 1));
|
|
shader_code_.push_back(ucp_dot_temp);
|
|
shader_code_.push_back(EncodeVectorSwizzledOperand(
|
|
D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1));
|
|
shader_code_.push_back(system_temp_position_);
|
|
shader_code_.push_back(EncodeVectorSwizzledOperand(
|
|
D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSwizzleXYZW, 3));
|
|
shader_code_.push_back(cbuffer_index_system_constants_);
|
|
shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants));
|
|
shader_code_.push_back(kSysConst_UserClipPlanes_Vec + i * 4 + j);
|
|
++stat_.instruction_count;
|
|
++stat_.float_instruction_count;
|
|
}
|
|
// Using movc rather than zeroing the planes in the constants because dp4
|
|
// would handle Infinity and NaN in an unexpected way.
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_AND) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(12));
|
|
shader_code_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, ucp_mask, 1));
|
|
shader_code_.push_back(ucp_enabled_temp);
|
|
shader_code_.push_back(EncodeVectorReplicatedOperand(
|
|
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(EncodeVectorSwizzledOperand(
|
|
D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0));
|
|
for (uint32_t j = 0; j < ucp_count; ++j) {
|
|
shader_code_.push_back(kSysFlag_UserClipPlane0 << (i * 4 + j));
|
|
}
|
|
for (uint32_t j = ucp_count; j < 4; ++j) {
|
|
shader_code_.push_back(0);
|
|
}
|
|
++stat_.instruction_count;
|
|
++stat_.uint_instruction_count;
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(12));
|
|
shader_code_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_OUTPUT, ucp_mask, 1));
|
|
shader_code_.push_back(uint32_t(InOutRegister::kVSOutClipDistance0123) + i);
|
|
shader_code_.push_back(EncodeVectorSwizzledOperand(
|
|
D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1));
|
|
shader_code_.push_back(ucp_enabled_temp);
|
|
shader_code_.push_back(EncodeVectorSwizzledOperand(
|
|
D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1));
|
|
shader_code_.push_back(ucp_dot_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_.movc_instruction_count;
|
|
}
|
|
// Release ucp_dot_temp and ucp_enabled_temp.
|
|
PopSystemTemp(2);
|
|
|
|
// Apply scale for drawing without a viewport, and also remap from OpenGL
|
|
// Z clip space to Direct3D if needed.
|
|
system_constants_used_ |= 1ull << kSysConst_NDCScale_Index;
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MUL) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9));
|
|
shader_code_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1));
|
|
shader_code_.push_back(system_temp_position_);
|
|
shader_code_.push_back(
|
|
EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1));
|
|
shader_code_.push_back(system_temp_position_);
|
|
shader_code_.push_back(EncodeVectorSwizzledOperand(
|
|
D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER,
|
|
kSysConst_NDCScale_Comp | ((kSysConst_NDCScale_Comp + 1) << 2) |
|
|
((kSysConst_NDCScale_Comp + 2) << 4),
|
|
3));
|
|
shader_code_.push_back(cbuffer_index_system_constants_);
|
|
shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants));
|
|
shader_code_.push_back(kSysConst_NDCScale_Vec);
|
|
++stat_.instruction_count;
|
|
++stat_.float_instruction_count;
|
|
|
|
// Reverse Z (Z = W - Z) if the viewport depth is inverted.
|
|
uint32_t reverse_z_temp = PushSystemTemp();
|
|
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, 0b0001, 1));
|
|
shader_code_.push_back(reverse_z_temp);
|
|
shader_code_.push_back(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1));
|
|
shader_code_.push_back(system_temp_position_);
|
|
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_NEG));
|
|
shader_code_.push_back(system_temp_position_);
|
|
++stat_.instruction_count;
|
|
++stat_.float_instruction_count;
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9));
|
|
shader_code_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0100, 1));
|
|
shader_code_.push_back(system_temp_position_);
|
|
shader_code_.push_back(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1));
|
|
shader_code_.push_back(ndc_control_temp);
|
|
shader_code_.push_back(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1));
|
|
shader_code_.push_back(reverse_z_temp);
|
|
shader_code_.push_back(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1));
|
|
shader_code_.push_back(system_temp_position_);
|
|
++stat_.instruction_count;
|
|
++stat_.movc_instruction_count;
|
|
// Release reverse_z_temp.
|
|
PopSystemTemp();
|
|
|
|
// Release ndc_control_temp.
|
|
PopSystemTemp();
|
|
|
|
// Apply offset (multiplied by W) for drawing without a viewport and for half
|
|
// pixel offset.
|
|
system_constants_used_ |= 1ull << kSysConst_NDCOffset_Index;
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MAD) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(11));
|
|
shader_code_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1));
|
|
shader_code_.push_back(system_temp_position_);
|
|
shader_code_.push_back(EncodeVectorSwizzledOperand(
|
|
D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER,
|
|
kSysConst_NDCOffset_Comp | ((kSysConst_NDCOffset_Comp + 1) << 2) |
|
|
((kSysConst_NDCOffset_Comp + 2) << 4),
|
|
3));
|
|
shader_code_.push_back(cbuffer_index_system_constants_);
|
|
shader_code_.push_back(uint32_t(CbufferRegister::kSystemConstants));
|
|
shader_code_.push_back(kSysConst_NDCOffset_Vec);
|
|
shader_code_.push_back(
|
|
EncodeVectorReplicatedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 3, 1));
|
|
shader_code_.push_back(system_temp_position_);
|
|
shader_code_.push_back(
|
|
EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1));
|
|
shader_code_.push_back(system_temp_position_);
|
|
++stat_.instruction_count;
|
|
++stat_.float_instruction_count;
|
|
|
|
// Write Z and W of the position to a separate attribute so ROV output can get
|
|
// per-sample depth.
|
|
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_OUTPUT, 0b0011, 1));
|
|
shader_code_.push_back(uint32_t(InOutRegister::kVSOutClipSpaceZW));
|
|
shader_code_.push_back(
|
|
EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b11111110, 1));
|
|
shader_code_.push_back(system_temp_position_);
|
|
++stat_.instruction_count;
|
|
++stat_.mov_instruction_count;
|
|
|
|
// Write the position to the output.
|
|
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_OUTPUT, 0b1111, 1));
|
|
shader_code_.push_back(uint32_t(InOutRegister::kVSOutPosition));
|
|
shader_code_.push_back(
|
|
EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, kSwizzleXYZW, 1));
|
|
shader_code_.push_back(system_temp_position_);
|
|
++stat_.instruction_count;
|
|
++stat_.mov_instruction_count;
|
|
}
|
|
|
|
void DxbcShaderTranslator::CompleteShaderCode() {
|
|
if (!is_depth_only_pixel_shader_) {
|
|
// Close the last exec, there's nothing to merge it with anymore, and we're
|
|
// closing upper-level flow control blocks.
|
|
CloseExecConditionals();
|
|
// Close the last label and the switch.
|
|
if (UseSwitchForControlFlow()) {
|
|
shader_code_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_BREAK) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1));
|
|
++stat_.instruction_count;
|
|
shader_code_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDSWITCH) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1));
|
|
++stat_.instruction_count;
|
|
} else {
|
|
shader_code_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1));
|
|
++stat_.instruction_count;
|
|
}
|
|
// End the main loop.
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_BREAK) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1));
|
|
++stat_.instruction_count;
|
|
shader_code_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDLOOP) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1));
|
|
++stat_.instruction_count;
|
|
|
|
// Release the following system temporary values so epilogue can reuse them:
|
|
// - system_temp_pv_.
|
|
// - system_temp_ps_pc_p0_a0_.
|
|
// - system_temp_aL_.
|
|
// - system_temp_loop_count_.
|
|
// - system_temp_grad_h_lod_.
|
|
// - system_temp_grad_v_.
|
|
PopSystemTemp(6);
|
|
|
|
// Write memexported data to the shared memory UAV.
|
|
ExportToMemory();
|
|
|
|
// Release memexport temporary registers.
|
|
for (int i = kMaxMemExports - 1; i >= 0; --i) {
|
|
if (system_temps_memexport_address_[i] == UINT32_MAX) {
|
|
continue;
|
|
}
|
|
// Release exported data registers.
|
|
for (int j = 4; j >= 0; --j) {
|
|
if (system_temps_memexport_data_[i][j] != UINT32_MAX) {
|
|
PopSystemTemp();
|
|
}
|
|
}
|
|
// Release the address register.
|
|
PopSystemTemp();
|
|
}
|
|
if (system_temp_memexport_written_ != UINT32_MAX) {
|
|
PopSystemTemp();
|
|
}
|
|
}
|
|
|
|
// Write stage-specific epilogue.
|
|
if (IsDxbcVertexOrDomainShader()) {
|
|
CompleteVertexOrDomainShader();
|
|
} else if (IsDxbcPixelShader()) {
|
|
CompletePixelShader();
|
|
}
|
|
|
|
// 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;
|
|
|
|
// Write subroutines - can only do this immediately after `ret`. They still
|
|
// need the global system temps, and can't allocate their own temps (since
|
|
// they may be called from anywhere and don't know anything about the caller's
|
|
// register allocation).
|
|
if (label_rov_depth_to_24bit_ != UINT32_MAX) {
|
|
CompleteShaderCode_ROV_DepthTo24BitSubroutine();
|
|
}
|
|
if (label_rov_depth_stencil_sample_ != UINT32_MAX) {
|
|
CompleteShaderCode_ROV_DepthStencilSampleSubroutine();
|
|
}
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
if (label_rov_color_sample_[i] != UINT32_MAX) {
|
|
CompleteShaderCode_ROV_ColorSampleSubroutine(i);
|
|
}
|
|
}
|
|
|
|
if (IsDxbcVertexOrDomainShader()) {
|
|
// Release system_temp_position_.
|
|
PopSystemTemp();
|
|
} else if (IsDxbcPixelShader()) {
|
|
// Release system_temps_color_.
|
|
for (int32_t i = 3; i >= 0; --i) {
|
|
if (writes_color_target(i)) {
|
|
PopSystemTemp();
|
|
}
|
|
}
|
|
if (edram_rov_used_) {
|
|
// Release system_temp_rov_params_ and system_temp_rov_depth_stencil_.
|
|
PopSystemTemp(2);
|
|
}
|
|
}
|
|
|
|
// Release system_temps_subroutine_.
|
|
PopSystemTemp(system_temps_subroutine_count_);
|
|
|
|
// Remap float constant indices if not indexed dynamically.
|
|
if (!float_constants_dynamic_indexed_ &&
|
|
!float_constant_index_offsets_.empty()) {
|
|
uint8_t float_constant_map[256] = {};
|
|
uint32_t float_constant_count = 0;
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
uint64_t float_constants_used = constant_register_map().float_bitmap[i];
|
|
uint32_t float_constant_index;
|
|
while (
|
|
xe::bit_scan_forward(float_constants_used, &float_constant_index)) {
|
|
float_constants_used &= ~(1ull << float_constant_index);
|
|
float_constant_map[i * 64 + float_constant_index] =
|
|
float_constant_count++;
|
|
}
|
|
}
|
|
size_t index_count = float_constant_index_offsets_.size();
|
|
for (size_t i = 0; i < index_count; ++i) {
|
|
uint32_t index_offset = float_constant_index_offsets_[i];
|
|
shader_code_[index_offset] =
|
|
float_constant_map[shader_code_[index_offset] & 255];
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<uint8_t> DxbcShaderTranslator::CompleteTranslation() {
|
|
// Write the code epilogue.
|
|
CompleteShaderCode();
|
|
|
|
shader_object_.clear();
|
|
|
|
uint32_t has_pcsg = IsDxbcDomainShader() ? 1 : 0;
|
|
|
|
// 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 or 6 chunks - RDEF, ISGN, optionally PCSG, OSGN, SHEX, STAT.
|
|
shader_object_.push_back(5 + has_pcsg);
|
|
// 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 Patch Constant SiGnature.
|
|
if (has_pcsg) {
|
|
chunk_position_dwords = uint32_t(shader_object_.size());
|
|
shader_object_[10] = chunk_position_dwords * sizeof(uint32_t);
|
|
shader_object_.push_back('GSCP');
|
|
shader_object_.push_back(0);
|
|
WritePatchConstantSignature();
|
|
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 + has_pcsg] = 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 + has_pcsg] = 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 + has_pcsg] = 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<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;
|
|
}
|
|
|
|
void DxbcShaderTranslator::EmitInstructionDisassembly() {
|
|
if (!cvars::dxbc_source_map) {
|
|
return;
|
|
}
|
|
|
|
const char* source = instruction_disassembly_buffer_.GetString();
|
|
uint32_t length = uint32_t(instruction_disassembly_buffer_.length());
|
|
// Trim leading spaces and trailing new line.
|
|
while (length != 0 && source[0] == ' ') {
|
|
++source;
|
|
--length;
|
|
}
|
|
while (length != 0 && source[length - 1] == '\n') {
|
|
--length;
|
|
}
|
|
if (length == 0) {
|
|
return;
|
|
}
|
|
|
|
uint32_t length_dwords =
|
|
(length + 1 + (sizeof(uint32_t) - 1)) / sizeof(uint32_t);
|
|
shader_code_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_CUSTOMDATA) |
|
|
ENCODE_D3D10_SB_CUSTOMDATA_CLASS(D3D10_SB_CUSTOMDATA_COMMENT));
|
|
shader_code_.push_back(2 + length_dwords);
|
|
size_t offset_dwords = shader_code_.size();
|
|
shader_code_.resize(offset_dwords + length_dwords);
|
|
char* target = reinterpret_cast<char*>(&shader_code_[offset_dwords]);
|
|
std::memcpy(target, source, length);
|
|
target[length] = '\0';
|
|
// Don't leave uninitialized data, and make sure multiple invocations of the
|
|
// translator for the same Xenos shader give the same DXBC.
|
|
std::memset(target + length + 1, 0xAB,
|
|
length_dwords * sizeof(uint32_t) - length - 1);
|
|
}
|
|
|
|
void DxbcShaderTranslator::LoadDxbcSourceOperand(
|
|
const InstructionOperand& operand, DxbcSourceOperand& dxbc_operand) {
|
|
// Initialize the values to their defaults.
|
|
dxbc_operand.type = DxbcSourceOperand::Type::kZerosOnes;
|
|
dxbc_operand.index = 0;
|
|
dxbc_operand.addressing_mode = InstructionStorageAddressingMode::kStatic;
|
|
dxbc_operand.swizzle = kSwizzleXYZW;
|
|
dxbc_operand.is_negated = operand.is_negated;
|
|
dxbc_operand.is_absolute_value = operand.is_absolute_value;
|
|
dxbc_operand.intermediate_register =
|
|
DxbcSourceOperand::kIntermediateRegisterNone;
|
|
|
|
if (operand.component_count == 0) {
|
|
// No components requested, probably totally invalid - give something more
|
|
// or less safe (zeros) and exit.
|
|
assert_always();
|
|
return;
|
|
}
|
|
|
|
// Make the DXBC swizzle, and also check whether there are any components with
|
|
// constant zero or one values (in this case, the operand will have to be
|
|
// loaded into the intermediate register) and if there are any real components
|
|
// at all (if there aren't, a literal can just be loaded).
|
|
uint32_t swizzle = 0;
|
|
uint32_t constant_components = 0;
|
|
uint32_t constant_component_values = 0;
|
|
for (uint32_t i = 0; i < uint32_t(operand.component_count); ++i) {
|
|
if (operand.components[i] <= SwizzleSource::kW) {
|
|
swizzle |= uint32_t(operand.components[i]) << (2 * i);
|
|
} else {
|
|
constant_components |= 1 << i;
|
|
if (operand.components[i] == SwizzleSource::k1) {
|
|
constant_component_values |= 1 << i;
|
|
}
|
|
}
|
|
}
|
|
// Replicate the last component's swizzle into all unused components.
|
|
uint32_t component_last = uint32_t(operand.component_count) - 1;
|
|
for (uint32_t i = uint32_t(operand.component_count); i < 4; ++i) {
|
|
swizzle |= ((swizzle >> (2 * component_last)) & 0x3) << (2 * i);
|
|
constant_components |= ((constant_components >> component_last) & 0x1) << i;
|
|
constant_component_values |=
|
|
((constant_component_values >> component_last) & 0x1) << i;
|
|
}
|
|
// If all components are constant, just write a literal.
|
|
if (constant_components == 0xF) {
|
|
dxbc_operand.index = constant_component_values;
|
|
return;
|
|
}
|
|
dxbc_operand.swizzle = swizzle;
|
|
|
|
// If the index is dynamic, choose where it's taken from.
|
|
uint32_t dynamic_address_register, dynamic_address_component;
|
|
if (operand.storage_addressing_mode ==
|
|
InstructionStorageAddressingMode::kAddressRelative) {
|
|
// Addressed by aL.x.
|
|
dynamic_address_register = system_temp_aL_;
|
|
dynamic_address_component = 0;
|
|
} else {
|
|
// Addressed by a0.
|
|
dynamic_address_register = system_temp_ps_pc_p0_a0_;
|
|
dynamic_address_component = 3;
|
|
}
|
|
|
|
// Actually load the operand.
|
|
switch (operand.storage_source) {
|
|
case InstructionStorageSource::kRegister:
|
|
// ***********************************************************************
|
|
// General-purpose register
|
|
// ***********************************************************************
|
|
if (uses_register_dynamic_addressing()) {
|
|
// GPRs are in x0 - need to load to the intermediate register (indexable
|
|
// temps are only accessible via mov load/store).
|
|
if (dxbc_operand.intermediate_register ==
|
|
DxbcSourceOperand::kIntermediateRegisterNone) {
|
|
dxbc_operand.intermediate_register = PushSystemTemp();
|
|
}
|
|
dxbc_operand.type = DxbcSourceOperand::Type::kIntermediateRegister;
|
|
if (operand.storage_addressing_mode ==
|
|
InstructionStorageAddressingMode::kStatic) {
|
|
shader_code_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(6));
|
|
shader_code_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1));
|
|
shader_code_.push_back(dxbc_operand.intermediate_register);
|
|
shader_code_.push_back(EncodeVectorSwizzledOperand(
|
|
D3D10_SB_OPERAND_TYPE_INDEXABLE_TEMP, kSwizzleXYZW, 2));
|
|
shader_code_.push_back(0);
|
|
shader_code_.push_back(uint32_t(operand.storage_index));
|
|
} else {
|
|
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(dxbc_operand.intermediate_register);
|
|
shader_code_.push_back(EncodeVectorSwizzledOperand(
|
|
D3D10_SB_OPERAND_TYPE_INDEXABLE_TEMP, kSwizzleXYZW, 2,
|
|
D3D10_SB_OPERAND_INDEX_IMMEDIATE32,
|
|
D3D10_SB_OPERAND_INDEX_IMMEDIATE32_PLUS_RELATIVE));
|
|
shader_code_.push_back(0);
|
|
shader_code_.push_back(uint32_t(operand.storage_index));
|
|
shader_code_.push_back(EncodeVectorSelectOperand(
|
|
D3D10_SB_OPERAND_TYPE_TEMP, dynamic_address_component, 1));
|
|
shader_code_.push_back(dynamic_address_register);
|
|
}
|
|
++stat_.instruction_count;
|
|
++stat_.array_instruction_count;
|
|
} else {
|
|
// GPRs are in r# - accessing directly.
|
|
assert_true(operand.storage_addressing_mode ==
|
|
InstructionStorageAddressingMode::kStatic);
|
|
dxbc_operand.type = DxbcSourceOperand::Type::kRegister;
|
|
dxbc_operand.index = uint32_t(operand.storage_index);
|
|
}
|
|
break;
|
|
|
|
case InstructionStorageSource::kConstantFloat:
|
|
// ***********************************************************************
|
|
// Float constant
|
|
// ***********************************************************************
|
|
if (cbuffer_index_float_constants_ == kCbufferIndexUnallocated) {
|
|
cbuffer_index_float_constants_ = cbuffer_count_++;
|
|
}
|
|
dxbc_operand.type = DxbcSourceOperand::Type::kConstantFloat;
|
|
dxbc_operand.index = uint32_t(operand.storage_index);
|
|
dxbc_operand.addressing_mode = operand.storage_addressing_mode;
|
|
if (operand.storage_addressing_mode !=
|
|
InstructionStorageAddressingMode::kStatic) {
|
|
float_constants_dynamic_indexed_ = true;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// Fall back to constant zeros for invalid types.
|
|
dxbc_operand.index = constant_component_values;
|
|
dxbc_operand.swizzle = kSwizzleXYZW;
|
|
return;
|
|
}
|
|
|
|
// If there are zeros or ones in the swizzle, force load the operand into the
|
|
// intermediate register (applying the swizzle and the modifiers), and then
|
|
// replace the components there.
|
|
if (constant_components != 0) {
|
|
if (dxbc_operand.intermediate_register ==
|
|
DxbcSourceOperand::kIntermediateRegisterNone) {
|
|
dxbc_operand.intermediate_register = PushSystemTemp();
|
|
}
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(
|
|
3 + DxbcSourceOperandLength(dxbc_operand)));
|
|
shader_code_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b1111, 1));
|
|
shader_code_.push_back(dxbc_operand.intermediate_register);
|
|
UseDxbcSourceOperand(dxbc_operand);
|
|
++stat_.instruction_count;
|
|
++stat_.mov_instruction_count;
|
|
|
|
// Write the constant components.
|
|
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,
|
|
constant_components, 1));
|
|
shader_code_.push_back(dxbc_operand.intermediate_register);
|
|
shader_code_.push_back(EncodeVectorSwizzledOperand(
|
|
D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0));
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
if (constant_component_values & (1 << i)) {
|
|
shader_code_.push_back(operand.is_negated ? 0xBF800000u : 0x3F800000u);
|
|
} else {
|
|
shader_code_.push_back(0);
|
|
}
|
|
}
|
|
++stat_.instruction_count;
|
|
++stat_.mov_instruction_count;
|
|
|
|
dxbc_operand.type = DxbcSourceOperand::Type::kIntermediateRegister;
|
|
// Swizzle and modifiers already applied.
|
|
dxbc_operand.swizzle = kSwizzleXYZW;
|
|
dxbc_operand.is_negated = false;
|
|
dxbc_operand.is_absolute_value = false;
|
|
}
|
|
}
|
|
|
|
uint32_t DxbcShaderTranslator::DxbcSourceOperandLength(
|
|
const DxbcSourceOperand& operand, bool negate, bool absolute) const {
|
|
uint32_t length;
|
|
switch (operand.type) {
|
|
case DxbcSourceOperand::Type::kRegister:
|
|
case DxbcSourceOperand::Type::kIntermediateRegister:
|
|
// Either a game register (for non-indexable GPRs) or the intermediate
|
|
// register with the data loaded (for indexable GPRs, bool and loop
|
|
// constants).
|
|
length = 2;
|
|
break;
|
|
case DxbcSourceOperand::Type::kConstantFloat:
|
|
if (operand.addressing_mode !=
|
|
InstructionStorageAddressingMode::kStatic) {
|
|
// Constant buffer, 3D index - immediate 0, immediate 1, immediate plus
|
|
// register 2.
|
|
length = 6;
|
|
} else {
|
|
// Constant buffer, 3D immediate index.
|
|
length = 4;
|
|
}
|
|
break;
|
|
default:
|
|
// Pre-negated literal of zeros and ones (no extension dword), or a
|
|
// totally invalid operand replaced by a literal.
|
|
return 5;
|
|
}
|
|
// Apply overrides (for instance, for subtraction). Xenos operand modifiers
|
|
// are ignored when forcing absolute value (though negated absolute can still
|
|
// be forced in this case).
|
|
if (!absolute) {
|
|
if (operand.is_negated) {
|
|
negate = !negate;
|
|
}
|
|
absolute |= operand.is_absolute_value;
|
|
}
|
|
// Modifier extension - neg/abs or non-uniform binding index.
|
|
if (negate || absolute) {
|
|
++length;
|
|
}
|
|
return length;
|
|
}
|
|
|
|
void DxbcShaderTranslator::UseDxbcSourceOperand(
|
|
const DxbcSourceOperand& operand, uint32_t additional_swizzle,
|
|
uint32_t select_component, bool negate, bool absolute) {
|
|
// Apply swizzle needed by the instruction implementation in addition to the
|
|
// operand swizzle.
|
|
uint32_t swizzle = 0;
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
uint32_t swizzle_component = (additional_swizzle >> (i * 2)) & 3;
|
|
swizzle |= ((operand.swizzle >> (swizzle_component * 2)) & 3) << (i * 2);
|
|
}
|
|
|
|
// Access either the whole vector or only one component of it, depending to
|
|
// what is needed.
|
|
uint32_t component_bits =
|
|
ENCODE_D3D10_SB_OPERAND_NUM_COMPONENTS(D3D10_SB_OPERAND_4_COMPONENT);
|
|
if (select_component <= 3) {
|
|
component_bits |= ENCODE_D3D10_SB_OPERAND_4_COMPONENT_SELECTION_MODE(
|
|
D3D10_SB_OPERAND_4_COMPONENT_SELECT_1_MODE) |
|
|
(((swizzle >> (select_component * 2)) & 0x3)
|
|
<< D3D10_SB_OPERAND_4_COMPONENT_SELECT_1_SHIFT);
|
|
} else {
|
|
component_bits |= ENCODE_D3D10_SB_OPERAND_4_COMPONENT_SELECTION_MODE(
|
|
D3D10_SB_OPERAND_4_COMPONENT_SWIZZLE_MODE) |
|
|
(swizzle << D3D10_SB_OPERAND_4_COMPONENT_SWIZZLE_SHIFT);
|
|
}
|
|
|
|
// Apply overrides (for instance, for subtraction). Xenos operand modifiers
|
|
// are ignored when forcing absolute value (though negated absolute can still
|
|
// be forced in this case).
|
|
if (!absolute) {
|
|
if (operand.is_negated) {
|
|
negate = !negate;
|
|
}
|
|
absolute |= operand.is_absolute_value;
|
|
}
|
|
// Build OperandToken1 for modifiers (negate, absolute, minimum precision,
|
|
// non-uniform binding index) - if it has any, it will be non-zero.
|
|
// NOTE: AMD GPUs or drivers do NOT support non-uniform constant buffer
|
|
// indices as of October 1, 2018 - they were causing significant skinned mesh
|
|
// corruption when Xenia used multiple descriptors for float constants rather
|
|
// than remapping.
|
|
uint32_t modifiers = 0;
|
|
if (negate && absolute) {
|
|
modifiers |= D3D10_SB_OPERAND_MODIFIER_ABSNEG
|
|
<< D3D10_SB_OPERAND_MODIFIER_SHIFT;
|
|
} else if (negate) {
|
|
modifiers |= D3D10_SB_OPERAND_MODIFIER_NEG
|
|
<< D3D10_SB_OPERAND_MODIFIER_SHIFT;
|
|
} else if (absolute) {
|
|
modifiers |= D3D10_SB_OPERAND_MODIFIER_ABS
|
|
<< D3D10_SB_OPERAND_MODIFIER_SHIFT;
|
|
}
|
|
if (modifiers != 0) {
|
|
// Mark the extension as containing modifiers.
|
|
modifiers |= ENCODE_D3D10_SB_EXTENDED_OPERAND_TYPE(
|
|
D3D10_SB_EXTENDED_OPERAND_MODIFIER);
|
|
}
|
|
uint32_t extended_bit = ENCODE_D3D10_SB_OPERAND_EXTENDED(modifiers);
|
|
|
|
// Actually write the operand tokens.
|
|
switch (operand.type) {
|
|
case DxbcSourceOperand::Type::kRegister:
|
|
shader_code_.push_back(
|
|
ENCODE_D3D10_SB_OPERAND_TYPE(D3D10_SB_OPERAND_TYPE_TEMP) |
|
|
ENCODE_D3D10_SB_OPERAND_INDEX_DIMENSION(D3D10_SB_OPERAND_INDEX_1D) |
|
|
ENCODE_D3D10_SB_OPERAND_INDEX_REPRESENTATION(
|
|
0, D3D10_SB_OPERAND_INDEX_IMMEDIATE32) |
|
|
component_bits | extended_bit);
|
|
if (modifiers != 0) {
|
|
shader_code_.push_back(modifiers);
|
|
}
|
|
shader_code_.push_back(operand.index);
|
|
break;
|
|
|
|
case DxbcSourceOperand::Type::kConstantFloat: {
|
|
bool is_static =
|
|
operand.addressing_mode == InstructionStorageAddressingMode::kStatic;
|
|
shader_code_.push_back(
|
|
ENCODE_D3D10_SB_OPERAND_TYPE(D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER) |
|
|
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, is_static ? D3D10_SB_OPERAND_INDEX_IMMEDIATE32
|
|
: D3D10_SB_OPERAND_INDEX_IMMEDIATE32_PLUS_RELATIVE) |
|
|
component_bits | extended_bit);
|
|
if (modifiers != 0) {
|
|
shader_code_.push_back(modifiers);
|
|
}
|
|
shader_code_.push_back(cbuffer_index_float_constants_);
|
|
shader_code_.push_back(uint32_t(CbufferRegister::kFloatConstants));
|
|
if (!float_constants_dynamic_indexed_) {
|
|
// If there's no dynamic indexing in the shader, constants are compacted
|
|
// and remapped. Store where the index has been written.
|
|
float_constant_index_offsets_.push_back(uint32_t(shader_code_.size()));
|
|
}
|
|
shader_code_.push_back(operand.index);
|
|
if (!is_static) {
|
|
uint32_t dynamic_address_register, dynamic_address_component;
|
|
if (operand.addressing_mode ==
|
|
InstructionStorageAddressingMode::kAddressRelative) {
|
|
// Addressed by aL.x.
|
|
dynamic_address_register = system_temp_aL_;
|
|
dynamic_address_component = 0;
|
|
} else {
|
|
// Addressed by a0.
|
|
dynamic_address_register = system_temp_ps_pc_p0_a0_;
|
|
dynamic_address_component = 3;
|
|
}
|
|
shader_code_.push_back(EncodeVectorSelectOperand(
|
|
D3D10_SB_OPERAND_TYPE_TEMP, dynamic_address_component, 1));
|
|
shader_code_.push_back(dynamic_address_register);
|
|
}
|
|
} break;
|
|
|
|
case DxbcSourceOperand::Type::kIntermediateRegister:
|
|
// Already loaded as float to the intermediate temporary register.
|
|
shader_code_.push_back(
|
|
ENCODE_D3D10_SB_OPERAND_TYPE(D3D10_SB_OPERAND_TYPE_TEMP) |
|
|
ENCODE_D3D10_SB_OPERAND_INDEX_DIMENSION(D3D10_SB_OPERAND_INDEX_1D) |
|
|
ENCODE_D3D10_SB_OPERAND_INDEX_REPRESENTATION(
|
|
0, D3D10_SB_OPERAND_INDEX_IMMEDIATE32) |
|
|
component_bits | extended_bit);
|
|
if (modifiers != 0) {
|
|
shader_code_.push_back(modifiers);
|
|
}
|
|
shader_code_.push_back(operand.intermediate_register);
|
|
break;
|
|
|
|
default:
|
|
// Only zeros and ones in the swizzle, or the safest replacement for an
|
|
// invalid operand (such as a fetch constant).
|
|
shader_code_.push_back(
|
|
ENCODE_D3D10_SB_OPERAND_TYPE(D3D10_SB_OPERAND_TYPE_IMMEDIATE32) |
|
|
ENCODE_D3D10_SB_OPERAND_INDEX_DIMENSION(D3D10_SB_OPERAND_INDEX_0D) |
|
|
component_bits);
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
if (operand.index & (1 << i)) {
|
|
shader_code_.push_back(negate ? 0xBF800000u : 0x3F800000u);
|
|
} else {
|
|
shader_code_.push_back(0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void DxbcShaderTranslator::UnloadDxbcSourceOperand(
|
|
const DxbcSourceOperand& operand) {
|
|
if (operand.intermediate_register !=
|
|
DxbcSourceOperand::kIntermediateRegisterNone) {
|
|
PopSystemTemp();
|
|
}
|
|
}
|
|
|
|
void DxbcShaderTranslator::StoreResult(const InstructionResult& result,
|
|
uint32_t reg, bool replicate_x,
|
|
bool can_store_memexport_address) {
|
|
if (result.storage_target == InstructionStorageTarget::kNone ||
|
|
!result.has_any_writes()) {
|
|
return;
|
|
}
|
|
|
|
// Validate memexport writes (Halo 3 has some weird invalid ones).
|
|
if (result.storage_target == InstructionStorageTarget::kExportAddress) {
|
|
if (!can_store_memexport_address || memexport_alloc_current_count_ == 0 ||
|
|
memexport_alloc_current_count_ > kMaxMemExports ||
|
|
system_temps_memexport_address_[memexport_alloc_current_count_ - 1] ==
|
|
UINT32_MAX) {
|
|
return;
|
|
}
|
|
} else if (result.storage_target == InstructionStorageTarget::kExportData) {
|
|
if (memexport_alloc_current_count_ == 0 ||
|
|
memexport_alloc_current_count_ > kMaxMemExports ||
|
|
system_temps_memexport_data_[memexport_alloc_current_count_ - 1]
|
|
[result.storage_index] == UINT32_MAX) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
uint32_t saturate_bit =
|
|
ENCODE_D3D10_SB_INSTRUCTION_SATURATE(result.is_clamped);
|
|
|
|
// Scalar targets get only one component.
|
|
if (result.storage_target == InstructionStorageTarget::kPointSize ||
|
|
result.storage_target == InstructionStorageTarget::kDepth) {
|
|
if (!result.write_mask[0]) {
|
|
return;
|
|
}
|
|
SwizzleSource component = result.components[0];
|
|
if (replicate_x && component <= SwizzleSource::kW) {
|
|
component = SwizzleSource::kX;
|
|
}
|
|
// Both r[imm32] and imm32 operands are 2 tokens long.
|
|
switch (result.storage_target) {
|
|
case InstructionStorageTarget::kPointSize:
|
|
shader_code_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5) | saturate_bit);
|
|
shader_code_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_OUTPUT, 0b0100, 1));
|
|
shader_code_.push_back(uint32_t(InOutRegister::kVSOutPointParameters));
|
|
break;
|
|
case InstructionStorageTarget::kDepth:
|
|
assert_true(writes_depth());
|
|
if (writes_depth()) {
|
|
if (edram_rov_used_) {
|
|
shader_code_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) |
|
|
ENCODE_D3D10_SB_INSTRUCTION_SATURATE(1) |
|
|
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_rov_depth_stencil_);
|
|
} else {
|
|
shader_code_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) |
|
|
ENCODE_D3D10_SB_INSTRUCTION_SATURATE(1) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(4));
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_OUTPUT_DEPTH, 0));
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
assert_unhandled_case(result.storage_target);
|
|
return;
|
|
}
|
|
if (component <= SwizzleSource::kW) {
|
|
shader_code_.push_back(EncodeVectorSelectOperand(
|
|
D3D10_SB_OPERAND_TYPE_TEMP, uint32_t(component), 1));
|
|
shader_code_.push_back(reg);
|
|
} else {
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
shader_code_.push_back(component == SwizzleSource::k1 ? 0x3F800000 : 0);
|
|
}
|
|
++stat_.instruction_count;
|
|
++stat_.mov_instruction_count;
|
|
return;
|
|
}
|
|
|
|
// Get the write masks and data required for loading of both the swizzled part
|
|
// and the constant (zero/one) part. The write mask is treated also as a read
|
|
// mask in DXBC, and `mov r0.zw, r1.xyzw` actually means r0.zw = r1.zw, not
|
|
// r0.zw = r1.xy.
|
|
uint32_t swizzle_mask = 0;
|
|
uint32_t swizzle_components = 0;
|
|
uint32_t constant_mask = 0;
|
|
uint32_t constant_values = 0;
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
if (!result.write_mask[i]) {
|
|
continue;
|
|
}
|
|
SwizzleSource component = result.components[i];
|
|
if (component <= SwizzleSource::kW) {
|
|
swizzle_mask |= 1 << i;
|
|
// If replicating X, just keep zero swizzle (XXXX).
|
|
if (!replicate_x) {
|
|
swizzle_components |= uint32_t(component) << (i * 2);
|
|
}
|
|
} else {
|
|
constant_mask |= 1 << i;
|
|
constant_values |= (component == SwizzleSource::k1 ? 1 : 0) << i;
|
|
}
|
|
}
|
|
|
|
bool is_static = result.storage_addressing_mode ==
|
|
InstructionStorageAddressingMode::kStatic;
|
|
// If the index is dynamic, choose where it's taken from.
|
|
uint32_t dynamic_address_register, dynamic_address_component;
|
|
if (result.storage_addressing_mode ==
|
|
InstructionStorageAddressingMode::kAddressRelative) {
|
|
// Addressed by aL.x.
|
|
dynamic_address_register = system_temp_aL_;
|
|
dynamic_address_component = 0;
|
|
} else {
|
|
// Addressed by a0.
|
|
dynamic_address_register = system_temp_ps_pc_p0_a0_;
|
|
dynamic_address_component = 3;
|
|
}
|
|
|
|
// Store both parts of the write (i == 0 - swizzled, i == 1 - constant).
|
|
for (uint32_t i = 0; i < 2; ++i) {
|
|
uint32_t mask = i == 0 ? swizzle_mask : constant_mask;
|
|
if (mask == 0) {
|
|
continue;
|
|
}
|
|
|
|
// r# for the swizzled part, 4-component imm32 for the constant part.
|
|
uint32_t source_length = i != 0 ? 5 : 2;
|
|
switch (result.storage_target) {
|
|
case InstructionStorageTarget::kRegister:
|
|
if (uses_register_dynamic_addressing()) {
|
|
++stat_.instruction_count;
|
|
++stat_.array_instruction_count;
|
|
shader_code_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH((is_static ? 4 : 6) +
|
|
source_length) |
|
|
saturate_bit);
|
|
shader_code_.push_back(EncodeVectorMaskedOperand(
|
|
D3D10_SB_OPERAND_TYPE_INDEXABLE_TEMP, mask, 2,
|
|
D3D10_SB_OPERAND_INDEX_IMMEDIATE32,
|
|
is_static ? D3D10_SB_OPERAND_INDEX_IMMEDIATE32
|
|
: D3D10_SB_OPERAND_INDEX_IMMEDIATE32_PLUS_RELATIVE));
|
|
shader_code_.push_back(0);
|
|
shader_code_.push_back(uint32_t(result.storage_index));
|
|
if (!is_static) {
|
|
shader_code_.push_back(EncodeVectorSelectOperand(
|
|
D3D10_SB_OPERAND_TYPE_TEMP, dynamic_address_component, 1));
|
|
shader_code_.push_back(dynamic_address_register);
|
|
}
|
|
} else {
|
|
assert_true(is_static);
|
|
++stat_.instruction_count;
|
|
++stat_.mov_instruction_count;
|
|
shader_code_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3 + source_length) |
|
|
saturate_bit);
|
|
shader_code_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, mask, 1));
|
|
shader_code_.push_back(uint32_t(result.storage_index));
|
|
}
|
|
break;
|
|
|
|
case InstructionStorageTarget::kInterpolant:
|
|
++stat_.instruction_count;
|
|
++stat_.mov_instruction_count;
|
|
shader_code_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3 + source_length) |
|
|
saturate_bit);
|
|
shader_code_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_OUTPUT, mask, 1));
|
|
shader_code_.push_back(uint32_t(InOutRegister::kVSOutInterpolators) +
|
|
uint32_t(result.storage_index));
|
|
break;
|
|
|
|
case InstructionStorageTarget::kPosition:
|
|
++stat_.instruction_count;
|
|
++stat_.mov_instruction_count;
|
|
shader_code_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3 + source_length) |
|
|
saturate_bit);
|
|
shader_code_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, mask, 1));
|
|
shader_code_.push_back(system_temp_position_);
|
|
break;
|
|
|
|
case InstructionStorageTarget::kExportAddress:
|
|
++stat_.instruction_count;
|
|
++stat_.mov_instruction_count;
|
|
shader_code_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3 + source_length) |
|
|
saturate_bit);
|
|
shader_code_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, mask, 1));
|
|
shader_code_.push_back(
|
|
system_temps_memexport_address_[memexport_alloc_current_count_ -
|
|
1]);
|
|
break;
|
|
|
|
case InstructionStorageTarget::kExportData:
|
|
++stat_.instruction_count;
|
|
++stat_.mov_instruction_count;
|
|
shader_code_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3 + source_length) |
|
|
saturate_bit);
|
|
shader_code_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, mask, 1));
|
|
shader_code_.push_back(
|
|
system_temps_memexport_data_[memexport_alloc_current_count_ - 1]
|
|
[uint32_t(result.storage_index)]);
|
|
break;
|
|
|
|
case InstructionStorageTarget::kColorTarget:
|
|
++stat_.instruction_count;
|
|
++stat_.mov_instruction_count;
|
|
shader_code_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3 + source_length) |
|
|
saturate_bit);
|
|
shader_code_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, mask, 1));
|
|
shader_code_.push_back(
|
|
system_temps_color_[uint32_t(result.storage_index)]);
|
|
break;
|
|
|
|
default:
|
|
continue;
|
|
}
|
|
|
|
if (i == 0) {
|
|
// Copy from the source r#.
|
|
shader_code_.push_back(EncodeVectorSwizzledOperand(
|
|
D3D10_SB_OPERAND_TYPE_TEMP, swizzle_components, 1));
|
|
shader_code_.push_back(reg);
|
|
} else {
|
|
// Load constants.
|
|
shader_code_.push_back(EncodeVectorSwizzledOperand(
|
|
D3D10_SB_OPERAND_TYPE_IMMEDIATE32, kSwizzleXYZW, 0));
|
|
for (uint32_t j = 0; j < 4; ++j) {
|
|
shader_code_.push_back((constant_values & (1 << j)) ? 0x3F800000 : 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (result.storage_target == InstructionStorageTarget::kExportData) {
|
|
// Mark that the eM# has been written to and needs to be exported.
|
|
uint32_t memexport_index = memexport_alloc_current_count_ - 1;
|
|
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, 1 << (memexport_index >> 2), 1));
|
|
shader_code_.push_back(system_temp_memexport_written_);
|
|
shader_code_.push_back(EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP,
|
|
memexport_index >> 2, 1));
|
|
shader_code_.push_back(system_temp_memexport_written_);
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
shader_code_.push_back(
|
|
1u << (uint32_t(result.storage_index) + ((memexport_index & 3) << 3)));
|
|
++stat_.instruction_count;
|
|
++stat_.uint_instruction_count;
|
|
}
|
|
|
|
if (edram_rov_used_ &&
|
|
result.storage_target == InstructionStorageTarget::kColorTarget) {
|
|
// For ROV output, mark that the color has been written to.
|
|
// According to:
|
|
// https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/dx9-graphics-reference-asm-ps-registers-output-color
|
|
// if a color target has been written to - including due to flow control -
|
|
// the render target must not be modified (the unwritten components of a
|
|
// written target are undefined, not sure if this behavior is respected on
|
|
// the real GPU, but the ROV code currently uses pre-packed masks to keep
|
|
// the old values, so preservation of components is not done).
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_OR) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7));
|
|
shader_code_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1));
|
|
shader_code_.push_back(system_temp_rov_params_);
|
|
shader_code_.push_back(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1));
|
|
shader_code_.push_back(system_temp_rov_params_);
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
shader_code_.push_back(1 << (8 + uint32_t(result.storage_index)));
|
|
++stat_.instruction_count;
|
|
++stat_.uint_instruction_count;
|
|
}
|
|
}
|
|
|
|
void DxbcShaderTranslator::UpdateExecConditionals(
|
|
ParsedExecInstruction::Type type, uint32_t bool_constant_index,
|
|
bool condition, bool emit_disassembly) {
|
|
// Check if we can merge the new exec with the previous one, or the jump with
|
|
// the previous exec. The instruction-level predicate check is also merged in
|
|
// this case.
|
|
bool merge = false;
|
|
if (type == ParsedExecInstruction::Type::kConditional) {
|
|
// Can merge conditional with conditional, as long as the bool constant and
|
|
// the expected values are the same.
|
|
if (cf_exec_bool_constant_ == bool_constant_index &&
|
|
cf_exec_bool_constant_condition_ == condition) {
|
|
merge = true;
|
|
}
|
|
} else if (type == ParsedExecInstruction::Type::kPredicated) {
|
|
// Can merge predicated with predicated if the conditions are the same and
|
|
// the previous exec hasn't modified the predicate register.
|
|
if (!cf_exec_predicate_written_ && cf_exec_predicated_ &&
|
|
cf_exec_predicate_condition_ == condition) {
|
|
merge = true;
|
|
}
|
|
} else {
|
|
// Can merge unconditional with unconditional.
|
|
if (cf_exec_bool_constant_ == kCfExecBoolConstantNone &&
|
|
!cf_exec_predicated_) {
|
|
merge = true;
|
|
}
|
|
}
|
|
|
|
if (merge) {
|
|
// Emit the disassembly for the exec/jump merged with the previous one.
|
|
if (emit_disassembly) {
|
|
EmitInstructionDisassembly();
|
|
}
|
|
return;
|
|
}
|
|
|
|
CloseExecConditionals();
|
|
|
|
// Emit the disassembly for the new exec/jump.
|
|
if (emit_disassembly) {
|
|
EmitInstructionDisassembly();
|
|
}
|
|
|
|
D3D10_SB_INSTRUCTION_TEST_BOOLEAN test =
|
|
condition ? D3D10_SB_INSTRUCTION_TEST_NONZERO
|
|
: D3D10_SB_INSTRUCTION_TEST_ZERO;
|
|
|
|
if (type == ParsedExecInstruction::Type::kConditional) {
|
|
uint32_t bool_constant_test_register = PushSystemTemp();
|
|
|
|
// Check the bool constant value.
|
|
if (cbuffer_index_bool_loop_constants_ == kCbufferIndexUnallocated) {
|
|
cbuffer_index_bool_loop_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, 0b0001, 1));
|
|
shader_code_.push_back(bool_constant_test_register);
|
|
shader_code_.push_back(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER,
|
|
(bool_constant_index >> 5) & 3, 3));
|
|
shader_code_.push_back(cbuffer_index_bool_loop_constants_);
|
|
shader_code_.push_back(uint32_t(CbufferRegister::kBoolLoopConstants));
|
|
shader_code_.push_back(bool_constant_index >> 7);
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
shader_code_.push_back(1u << (bool_constant_index & 31));
|
|
++stat_.instruction_count;
|
|
++stat_.uint_instruction_count;
|
|
|
|
// Open the new `if`.
|
|
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, 0, 1));
|
|
shader_code_.push_back(bool_constant_test_register);
|
|
++stat_.instruction_count;
|
|
++stat_.dynamic_flow_control_count;
|
|
|
|
// Release bool_constant_test_register.
|
|
PopSystemTemp();
|
|
|
|
cf_exec_bool_constant_ = bool_constant_index;
|
|
cf_exec_bool_constant_condition_ = condition;
|
|
} else if (type == ParsedExecInstruction::Type::kPredicated) {
|
|
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, 2, 1));
|
|
shader_code_.push_back(system_temp_ps_pc_p0_a0_);
|
|
++stat_.instruction_count;
|
|
++stat_.dynamic_flow_control_count;
|
|
|
|
cf_exec_predicated_ = true;
|
|
cf_exec_predicate_condition_ = condition;
|
|
}
|
|
}
|
|
|
|
void DxbcShaderTranslator::CloseExecConditionals() {
|
|
// Within the exec - instruction-level predicate check.
|
|
CloseInstructionPredication();
|
|
// Exec level.
|
|
if (cf_exec_bool_constant_ != kCfExecBoolConstantNone ||
|
|
cf_exec_predicated_) {
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1));
|
|
++stat_.instruction_count;
|
|
cf_exec_bool_constant_ = kCfExecBoolConstantNone;
|
|
cf_exec_predicated_ = false;
|
|
}
|
|
// Nothing relies on the predicate value being unchanged now.
|
|
cf_exec_predicate_written_ = false;
|
|
}
|
|
|
|
void DxbcShaderTranslator::UpdateInstructionPredication(bool predicated,
|
|
bool condition,
|
|
bool emit_disassembly) {
|
|
if (predicated) {
|
|
if (cf_instruction_predicate_if_open_) {
|
|
if (cf_instruction_predicate_condition_ == condition) {
|
|
// Already in the needed instruction-level `if`.
|
|
if (emit_disassembly) {
|
|
EmitInstructionDisassembly();
|
|
}
|
|
return;
|
|
}
|
|
CloseInstructionPredication();
|
|
}
|
|
|
|
// Emit the disassembly before opening (or not opening) the new conditional.
|
|
if (emit_disassembly) {
|
|
EmitInstructionDisassembly();
|
|
}
|
|
|
|
// If the instruction predicate condition is the same as the exec predicate
|
|
// condition, no need to open a check. However, if there was a `setp` prior
|
|
// to this instruction, the predicate value now may be different than it was
|
|
// in the beginning of the exec.
|
|
if (!cf_exec_predicate_written_ && cf_exec_predicated_ &&
|
|
cf_exec_predicate_condition_ == condition) {
|
|
return;
|
|
}
|
|
|
|
D3D10_SB_INSTRUCTION_TEST_BOOLEAN test =
|
|
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, 2, 1));
|
|
shader_code_.push_back(system_temp_ps_pc_p0_a0_);
|
|
++stat_.instruction_count;
|
|
++stat_.dynamic_flow_control_count;
|
|
|
|
cf_instruction_predicate_if_open_ = true;
|
|
cf_instruction_predicate_condition_ = condition;
|
|
} else {
|
|
CloseInstructionPredication();
|
|
if (emit_disassembly) {
|
|
EmitInstructionDisassembly();
|
|
}
|
|
}
|
|
}
|
|
|
|
void DxbcShaderTranslator::CloseInstructionPredication() {
|
|
if (cf_instruction_predicate_if_open_) {
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1));
|
|
++stat_.instruction_count;
|
|
cf_instruction_predicate_if_open_ = false;
|
|
}
|
|
}
|
|
|
|
void DxbcShaderTranslator::JumpToLabel(uint32_t address) {
|
|
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, 0b0010, 1));
|
|
shader_code_.push_back(system_temp_ps_pc_p0_a0_);
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
shader_code_.push_back(address);
|
|
++stat_.instruction_count;
|
|
++stat_.mov_instruction_count;
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_CONTINUE) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1));
|
|
++stat_.instruction_count;
|
|
}
|
|
|
|
void DxbcShaderTranslator::ProcessLabel(uint32_t cf_index) {
|
|
if (cf_index == 0) {
|
|
// 0 already added in the beginning.
|
|
return;
|
|
}
|
|
|
|
// Close flow control on the deeper levels below - prevent attempts to merge
|
|
// execs across labels.
|
|
CloseExecConditionals();
|
|
|
|
if (UseSwitchForControlFlow()) {
|
|
// Fallthrough to the label from the previous one on the next iteration if
|
|
// no `continue` was done. Can't simply fallthrough because in DXBC, a
|
|
// non-empty switch case must end with a break.
|
|
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, 0b0010, 1));
|
|
shader_code_.push_back(system_temp_ps_pc_p0_a0_);
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
shader_code_.push_back(cf_index);
|
|
++stat_.instruction_count;
|
|
++stat_.mov_instruction_count;
|
|
shader_code_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_CONTINUE) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1));
|
|
++stat_.instruction_count;
|
|
// Close the previous label.
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_BREAK) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1));
|
|
++stat_.instruction_count;
|
|
// Go to the next label.
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_CASE) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3));
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
shader_code_.push_back(cf_index);
|
|
++stat_.instruction_count;
|
|
++stat_.static_flow_control_count;
|
|
} else {
|
|
// Close the previous label.
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1));
|
|
++stat_.instruction_count;
|
|
|
|
// pc <= cf_index
|
|
uint32_t test_register = PushSystemTemp();
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_UGE) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7));
|
|
shader_code_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1));
|
|
shader_code_.push_back(test_register);
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
shader_code_.push_back(cf_index);
|
|
shader_code_.push_back(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1));
|
|
shader_code_.push_back(system_temp_ps_pc_p0_a0_);
|
|
++stat_.instruction_count;
|
|
++stat_.uint_instruction_count;
|
|
// if (pc <= cf_index)
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_IF) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3) |
|
|
ENCODE_D3D10_SB_INSTRUCTION_TEST_BOOLEAN(
|
|
D3D10_SB_INSTRUCTION_TEST_NONZERO));
|
|
shader_code_.push_back(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1));
|
|
shader_code_.push_back(test_register);
|
|
++stat_.instruction_count;
|
|
++stat_.dynamic_flow_control_count;
|
|
PopSystemTemp();
|
|
}
|
|
}
|
|
|
|
void DxbcShaderTranslator::ProcessExecInstructionBegin(
|
|
const ParsedExecInstruction& instr) {
|
|
if (cvars::dxbc_source_map) {
|
|
instruction_disassembly_buffer_.Reset();
|
|
instr.Disassemble(&instruction_disassembly_buffer_);
|
|
// Will be emitted by UpdateExecConditionals.
|
|
}
|
|
UpdateExecConditionals(instr.type, instr.bool_constant_index, instr.condition,
|
|
true);
|
|
// TODO(Triang3l): Find out what PredicateClean=false in exec actually means
|
|
// (execs containing setp have PredicateClean=false, it possibly means that
|
|
// the predicate is dirty after the exec).
|
|
}
|
|
|
|
void DxbcShaderTranslator::ProcessExecInstructionEnd(
|
|
const ParsedExecInstruction& instr) {
|
|
// TODO(Triang3l): Check whether is_end is conditional or not.
|
|
if (instr.is_end) {
|
|
// Break out of the main loop.
|
|
CloseInstructionPredication();
|
|
if (UseSwitchForControlFlow()) {
|
|
// Write an invalid value to pc.
|
|
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, 0b0010, 1));
|
|
shader_code_.push_back(system_temp_ps_pc_p0_a0_);
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
shader_code_.push_back(0xFFFFFFFFu);
|
|
++stat_.instruction_count;
|
|
++stat_.mov_instruction_count;
|
|
// Go to the next iteration, where switch cases won't be reached.
|
|
shader_code_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_CONTINUE) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1));
|
|
++stat_.instruction_count;
|
|
} else {
|
|
shader_code_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_BREAK) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1));
|
|
++stat_.instruction_count;
|
|
}
|
|
}
|
|
}
|
|
|
|
void DxbcShaderTranslator::ProcessLoopStartInstruction(
|
|
const ParsedLoopStartInstruction& instr) {
|
|
// loop il<idx>, L<idx> - loop with loop data il<idx>, end @ L<idx>
|
|
|
|
// Loop control is outside execs - actually close the last exec.
|
|
CloseExecConditionals();
|
|
|
|
if (cvars::dxbc_source_map) {
|
|
instruction_disassembly_buffer_.Reset();
|
|
instr.Disassemble(&instruction_disassembly_buffer_);
|
|
EmitInstructionDisassembly();
|
|
}
|
|
|
|
uint32_t loop_count_and_aL = PushSystemTemp();
|
|
|
|
// Count (as uint) in bits 0:7 of the loop constant, aL in 8:15.
|
|
if (cbuffer_index_bool_loop_constants_ == kCbufferIndexUnallocated) {
|
|
cbuffer_index_bool_loop_constants_ = cbuffer_count_++;
|
|
}
|
|
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(loop_count_and_aL);
|
|
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(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(8);
|
|
shader_code_.push_back(0);
|
|
shader_code_.push_back(0);
|
|
shader_code_.push_back(EncodeVectorReplicatedOperand(
|
|
D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, instr.loop_constant_index & 3, 3));
|
|
shader_code_.push_back(cbuffer_index_bool_loop_constants_);
|
|
shader_code_.push_back(uint32_t(CbufferRegister::kBoolLoopConstants));
|
|
// 2 because of bool constants.
|
|
shader_code_.push_back(2 + (instr.loop_constant_index >> 2));
|
|
++stat_.instruction_count;
|
|
++stat_.uint_instruction_count;
|
|
|
|
// Push the count to the loop count stack - move XYZ to YZW and set X to this
|
|
// loop 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, 0b1110, 1));
|
|
shader_code_.push_back(system_temp_loop_count_);
|
|
shader_code_.push_back(
|
|
EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b10010000, 1));
|
|
shader_code_.push_back(system_temp_loop_count_);
|
|
++stat_.instruction_count;
|
|
++stat_.mov_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(system_temp_loop_count_);
|
|
shader_code_.push_back(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1));
|
|
shader_code_.push_back(loop_count_and_aL);
|
|
++stat_.instruction_count;
|
|
++stat_.mov_instruction_count;
|
|
|
|
// Push aL - keep the same value as in the previous loop if repeating, or the
|
|
// new one otherwise.
|
|
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, 0b1111, 1));
|
|
shader_code_.push_back(system_temp_aL_);
|
|
shader_code_.push_back(
|
|
EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b10010000, 1));
|
|
shader_code_.push_back(system_temp_aL_);
|
|
++stat_.instruction_count;
|
|
++stat_.mov_instruction_count;
|
|
if (!instr.is_repeat) {
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5));
|
|
shader_code_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1));
|
|
shader_code_.push_back(system_temp_aL_);
|
|
shader_code_.push_back(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 1, 1));
|
|
shader_code_.push_back(loop_count_and_aL);
|
|
++stat_.instruction_count;
|
|
++stat_.mov_instruction_count;
|
|
}
|
|
|
|
// Release loop_count_and_aL.
|
|
PopSystemTemp();
|
|
|
|
// Short-circuit if loop counter is 0.
|
|
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, 0, 1));
|
|
shader_code_.push_back(system_temp_loop_count_);
|
|
++stat_.instruction_count;
|
|
++stat_.dynamic_flow_control_count;
|
|
JumpToLabel(instr.loop_skip_address);
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1));
|
|
++stat_.instruction_count;
|
|
}
|
|
|
|
void DxbcShaderTranslator::ProcessLoopEndInstruction(
|
|
const ParsedLoopEndInstruction& instr) {
|
|
// endloop il<idx>, L<idx> - end loop w/ data il<idx>, head @ L<idx>
|
|
|
|
// Loop control is outside execs - actually close the last exec.
|
|
CloseExecConditionals();
|
|
|
|
if (cvars::dxbc_source_map) {
|
|
instruction_disassembly_buffer_.Reset();
|
|
instr.Disassemble(&instruction_disassembly_buffer_);
|
|
EmitInstructionDisassembly();
|
|
}
|
|
|
|
// Subtract 1 from the loop counter.
|
|
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_loop_count_);
|
|
shader_code_.push_back(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1));
|
|
shader_code_.push_back(system_temp_loop_count_);
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
shader_code_.push_back(uint32_t(-1));
|
|
++stat_.instruction_count;
|
|
++stat_.int_instruction_count;
|
|
|
|
// Break case.
|
|
|
|
if (instr.is_predicated_break) {
|
|
// if (loop_count.x == 0 || [!]p0)
|
|
uint32_t break_case_temp = PushSystemTemp();
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOVC) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(9));
|
|
shader_code_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0001, 1));
|
|
shader_code_.push_back(break_case_temp);
|
|
shader_code_.push_back(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 2, 1));
|
|
shader_code_.push_back(system_temp_ps_pc_p0_a0_);
|
|
if (instr.predicate_condition) {
|
|
// If p0 is non-zero, set the test value to 0 (since if_z is used,
|
|
// otherwise check if the loop counter is zero).
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
shader_code_.push_back(0);
|
|
}
|
|
shader_code_.push_back(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1));
|
|
shader_code_.push_back(system_temp_loop_count_);
|
|
if (!instr.predicate_condition) {
|
|
// If p0 is zero, set the test value to 0 (since if_z is used, otherwise
|
|
// check if the loop counter is zero).
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
shader_code_.push_back(0);
|
|
}
|
|
++stat_.instruction_count;
|
|
++stat_.movc_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_ZERO) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3));
|
|
shader_code_.push_back(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1));
|
|
shader_code_.push_back(break_case_temp);
|
|
PopSystemTemp();
|
|
} else {
|
|
// if (loop_count.x == 0)
|
|
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, 0, 1));
|
|
shader_code_.push_back(system_temp_loop_count_);
|
|
}
|
|
++stat_.instruction_count;
|
|
++stat_.dynamic_flow_control_count;
|
|
|
|
// Pop the current loop off the stack, move YZW to XYZ and set W to 0.
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(5));
|
|
shader_code_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b0111, 1));
|
|
shader_code_.push_back(system_temp_loop_count_);
|
|
shader_code_.push_back(
|
|
EncodeVectorSwizzledOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0b11111001, 1));
|
|
shader_code_.push_back(system_temp_loop_count_);
|
|
++stat_.instruction_count;
|
|
++stat_.mov_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, 0b1000, 1));
|
|
shader_code_.push_back(system_temp_loop_count_);
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
shader_code_.push_back(0);
|
|
++stat_.instruction_count;
|
|
++stat_.mov_instruction_count;
|
|
|
|
// Now going to fall through to the next exec (no need to jump).
|
|
|
|
// Continue case.
|
|
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ELSE) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1));
|
|
++stat_.instruction_count;
|
|
|
|
uint32_t aL_add_temp = PushSystemTemp();
|
|
|
|
// Extract the value to add to aL (in bits 16:23 of the loop constant).
|
|
if (cbuffer_index_bool_loop_constants_ == kCbufferIndexUnallocated) {
|
|
cbuffer_index_bool_loop_constants_ = cbuffer_count_++;
|
|
}
|
|
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(aL_add_temp);
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
shader_code_.push_back(8);
|
|
shader_code_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
shader_code_.push_back(16);
|
|
shader_code_.push_back(EncodeVectorSelectOperand(
|
|
D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, instr.loop_constant_index & 3, 3));
|
|
shader_code_.push_back(cbuffer_index_bool_loop_constants_);
|
|
shader_code_.push_back(uint32_t(CbufferRegister::kBoolLoopConstants));
|
|
// 2 because of bool constants.
|
|
shader_code_.push_back(2 + (instr.loop_constant_index >> 2));
|
|
++stat_.instruction_count;
|
|
++stat_.uint_instruction_count;
|
|
|
|
// Add the needed value to aL.
|
|
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_aL_);
|
|
shader_code_.push_back(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1));
|
|
shader_code_.push_back(system_temp_aL_);
|
|
shader_code_.push_back(
|
|
EncodeVectorSelectOperand(D3D10_SB_OPERAND_TYPE_TEMP, 0, 1));
|
|
shader_code_.push_back(aL_add_temp);
|
|
++stat_.instruction_count;
|
|
++stat_.int_instruction_count;
|
|
|
|
// Release aL_add_temp.
|
|
PopSystemTemp();
|
|
|
|
// Jump back to the beginning of the loop body.
|
|
JumpToLabel(instr.loop_body_address);
|
|
|
|
shader_code_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_ENDIF) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1));
|
|
++stat_.instruction_count;
|
|
}
|
|
|
|
void DxbcShaderTranslator::ProcessJumpInstruction(
|
|
const ParsedJumpInstruction& instr) {
|
|
if (cvars::dxbc_source_map) {
|
|
instruction_disassembly_buffer_.Reset();
|
|
instr.Disassemble(&instruction_disassembly_buffer_);
|
|
// Will be emitted by UpdateExecConditionals.
|
|
}
|
|
|
|
// Treat like exec, merge with execs if possible, since it's an if too.
|
|
ParsedExecInstruction::Type type;
|
|
if (instr.type == ParsedJumpInstruction::Type::kConditional) {
|
|
type = ParsedExecInstruction::Type::kConditional;
|
|
} else if (instr.type == ParsedJumpInstruction::Type::kPredicated) {
|
|
type = ParsedExecInstruction::Type::kPredicated;
|
|
} else {
|
|
type = ParsedExecInstruction::Type::kUnconditional;
|
|
}
|
|
UpdateExecConditionals(type, instr.bool_constant_index, instr.condition,
|
|
true);
|
|
|
|
// UpdateExecConditionals may not necessarily close the instruction-level
|
|
// predicate check (it's not necessary if the execs are merged), but here the
|
|
// instruction itself is on the flow control level, so the predicate check is
|
|
// on the flow control level too.
|
|
CloseInstructionPredication();
|
|
|
|
JumpToLabel(instr.target_address);
|
|
}
|
|
|
|
void DxbcShaderTranslator::ProcessAllocInstruction(
|
|
const ParsedAllocInstruction& instr) {
|
|
if (cvars::dxbc_source_map) {
|
|
instruction_disassembly_buffer_.Reset();
|
|
instr.Disassemble(&instruction_disassembly_buffer_);
|
|
EmitInstructionDisassembly();
|
|
}
|
|
|
|
if (instr.type == AllocType::kMemory) {
|
|
++memexport_alloc_current_count_;
|
|
}
|
|
}
|
|
|
|
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, 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);
|
|
// Don't leave uninitialized data, and make sure multiple invocations of the
|
|
// translator for the same Xenos shader give the same DXBC.
|
|
std::memset(reinterpret_cast<uint8_t*>(&dest[dest_position]) + size, 0xAB,
|
|
size_aligned - size);
|
|
return uint32_t(size_aligned);
|
|
}
|
|
|
|
const DxbcShaderTranslator::RdefType DxbcShaderTranslator::rdef_types_[size_t(
|
|
DxbcShaderTranslator::RdefTypeIndex::kCount)] = {
|
|
// kFloat
|
|
{"float", 0, 3, 1, 1, 0, 0, RdefTypeIndex::kUnknown, nullptr},
|
|
// kFloat2
|
|
{"float2", 1, 3, 1, 2, 0, 0, RdefTypeIndex::kUnknown, nullptr},
|
|
// kFloat3
|
|
{"float3", 1, 3, 1, 3, 0, 0, RdefTypeIndex::kUnknown, nullptr},
|
|
// kFloat4
|
|
{"float4", 1, 3, 1, 4, 0, 0, RdefTypeIndex::kUnknown, nullptr},
|
|
// kInt
|
|
{"int", 0, 2, 1, 1, 0, 0, RdefTypeIndex::kUnknown, nullptr},
|
|
// kUint
|
|
{"uint", 0, 19, 1, 1, 0, 0, RdefTypeIndex::kUnknown, nullptr},
|
|
// kUint2
|
|
{"uint2", 1, 19, 1, 2, 0, 0, RdefTypeIndex::kUnknown, nullptr},
|
|
// kUint4
|
|
{"uint4", 1, 19, 1, 4, 0, 0, RdefTypeIndex::kUnknown, nullptr},
|
|
// kFloat4Array4
|
|
{nullptr, 1, 3, 1, 4, 4, 0, RdefTypeIndex::kFloat4, nullptr},
|
|
// kFloat4Array6
|
|
{nullptr, 1, 3, 1, 4, 6, 0, RdefTypeIndex::kFloat4, nullptr},
|
|
// kFloat4ConstantArray - float constants - size written dynamically.
|
|
{nullptr, 1, 3, 1, 4, 0, 0, RdefTypeIndex::kFloat4, nullptr},
|
|
// kUint4Array2
|
|
{nullptr, 1, 19, 1, 4, 2, 0, RdefTypeIndex::kUint4, nullptr},
|
|
// kUint4Array8
|
|
{nullptr, 1, 19, 1, 4, 8, 0, RdefTypeIndex::kUint4, nullptr},
|
|
// kUint4Array48
|
|
{nullptr, 1, 19, 1, 4, 48, 0, RdefTypeIndex::kUint4, nullptr},
|
|
};
|
|
|
|
const DxbcShaderTranslator::SystemConstantRdef DxbcShaderTranslator::
|
|
system_constant_rdef_[DxbcShaderTranslator::kSysConst_Count] = {
|
|
{"xe_flags", RdefTypeIndex::kUint, 4},
|
|
{"xe_line_loop_closing_index", RdefTypeIndex::kUint, 4},
|
|
{"xe_vertex_index_endian_and_edge_factors", RdefTypeIndex::kUint, 4},
|
|
{"xe_vertex_base_index", RdefTypeIndex::kInt, 4},
|
|
|
|
{"xe_user_clip_planes", RdefTypeIndex::kFloat4Array6, 96},
|
|
|
|
{"xe_ndc_scale", RdefTypeIndex::kFloat3, 12},
|
|
{"xe_pixel_pos_reg", RdefTypeIndex::kUint, 4},
|
|
|
|
{"xe_ndc_offset", RdefTypeIndex::kFloat3, 12},
|
|
{"xe_pixel_half_pixel_offset", RdefTypeIndex::kFloat, 4},
|
|
|
|
{"xe_point_size", RdefTypeIndex::kFloat2, 8},
|
|
{"xe_point_size_min_max", RdefTypeIndex::kFloat2, 8},
|
|
|
|
{"xe_point_screen_to_ndc", RdefTypeIndex::kFloat2, 8},
|
|
{"xe_sample_count_log2", RdefTypeIndex::kUint2, 8},
|
|
|
|
{"xe_alpha_test_reference", RdefTypeIndex::kFloat, 4},
|
|
{"xe_edram_resolution_square_scale", RdefTypeIndex::kUint, 4},
|
|
{"xe_edram_pitch_tiles", RdefTypeIndex::kUint, 4},
|
|
{"xe_edram_depth_base_dwords", RdefTypeIndex::kUint, 4},
|
|
|
|
{"xe_color_exp_bias", RdefTypeIndex::kFloat4, 16},
|
|
|
|
{"xe_color_output_map", RdefTypeIndex::kUint4, 16},
|
|
|
|
{"xe_tessellation_factor_range", RdefTypeIndex::kFloat2, 8},
|
|
{"xe_edram_depth_range", RdefTypeIndex::kFloat2, 8},
|
|
|
|
{"xe_edram_poly_offset_front", RdefTypeIndex::kFloat2, 8},
|
|
{"xe_edram_poly_offset_back", RdefTypeIndex::kFloat2, 8},
|
|
|
|
{"xe_edram_stencil", RdefTypeIndex::kUint4Array2, 32},
|
|
|
|
{"xe_edram_rt_base_dwords_scaled", RdefTypeIndex::kUint4, 16},
|
|
|
|
{"xe_edram_rt_format_flags", RdefTypeIndex::kUint4, 16},
|
|
|
|
{"xe_edram_rt_clamp", RdefTypeIndex::kFloat4Array4, 64},
|
|
|
|
{"xe_edram_rt_keep_mask", RdefTypeIndex::kUint4Array2, 32},
|
|
|
|
{"xe_edram_rt_blend_factors_ops", RdefTypeIndex::kUint4, 16},
|
|
|
|
{"xe_edram_blend_constant", RdefTypeIndex::kFloat4, 16},
|
|
};
|
|
|
|
void DxbcShaderTranslator::WriteResourceDefinitions() {
|
|
// ***************************************************************************
|
|
// Preparation
|
|
// ***************************************************************************
|
|
|
|
// Float constant count.
|
|
uint32_t float_constant_count = 0;
|
|
if (cbuffer_index_float_constants_ != kCbufferIndexUnallocated) {
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
float_constant_count +=
|
|
xe::bit_count(constant_register_map().float_bitmap[i]);
|
|
}
|
|
}
|
|
|
|
uint32_t chunk_position_dwords = uint32_t(shader_object_.size());
|
|
uint32_t new_offset;
|
|
|
|
// ***************************************************************************
|
|
// Header
|
|
// ***************************************************************************
|
|
|
|
// Constant buffer count.
|
|
shader_object_.push_back(cbuffer_count_);
|
|
// Constant buffer offset (set later).
|
|
shader_object_.push_back(0);
|
|
// Bound resource count (samplers, SRV, UAV, CBV).
|
|
uint32_t resource_count = cbuffer_count_;
|
|
if (!is_depth_only_pixel_shader_) {
|
|
// + 2 for shared memory SRV and UAV (vfetches can appear in pixel shaders
|
|
// too, and the UAV is needed for memexport, however, the choice between
|
|
// SRV and UAV is per-pipeline, not per-shader - a resource can't be in a
|
|
// read-only state (SRV, IBV) if it's in a read/write state such as UAV).
|
|
resource_count +=
|
|
uint32_t(sampler_bindings_.size()) + 2 + uint32_t(texture_srvs_.size());
|
|
}
|
|
if (IsDxbcPixelShader() && edram_rov_used_) {
|
|
// EDRAM.
|
|
++resource_count;
|
|
}
|
|
shader_object_.push_back(resource_count);
|
|
// Bound resource buffer offset (set later).
|
|
shader_object_.push_back(0);
|
|
if (IsDxbcVertexShader()) {
|
|
// vs_5_1
|
|
shader_object_.push_back(0xFFFE0501u);
|
|
} else if (IsDxbcDomainShader()) {
|
|
// ds_5_1
|
|
shader_object_.push_back(0x44530501u);
|
|
} else {
|
|
assert_true(IsDxbcPixelShader());
|
|
// 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));
|
|
if (RdefTypeIndex(i) == RdefTypeIndex::kFloat4ConstantArray) {
|
|
// Declaring a 0-sized array may not be safe, so write something valid
|
|
// even if they aren't used.
|
|
shader_object_.push_back(std::max(float_constant_count, 1u));
|
|
} else {
|
|
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]);
|
|
}
|
|
|
|
#if 0
|
|
// Structure members. Structures are not used currently, but were used in the
|
|
// past, so the code is kept here.
|
|
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);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// ***************************************************************************
|
|
// Constants
|
|
// ***************************************************************************
|
|
|
|
// Names.
|
|
new_offset = (uint32_t(shader_object_.size()) - chunk_position_dwords) *
|
|
sizeof(uint32_t);
|
|
uint32_t constant_name_offsets_system[kSysConst_Count];
|
|
if (cbuffer_index_system_constants_ != kCbufferIndexUnallocated) {
|
|
for (uint32_t i = 0; i < kSysConst_Count; ++i) {
|
|
constant_name_offsets_system[i] = new_offset;
|
|
new_offset += AppendString(shader_object_, system_constant_rdef_[i].name);
|
|
}
|
|
}
|
|
uint32_t constant_name_offset_float = new_offset;
|
|
if (cbuffer_index_float_constants_ != kCbufferIndexUnallocated) {
|
|
new_offset += AppendString(shader_object_, "xe_float_constants");
|
|
}
|
|
uint32_t constant_name_offset_bool = new_offset;
|
|
uint32_t constant_name_offset_loop = constant_name_offset_bool;
|
|
if (cbuffer_index_bool_loop_constants_ != kCbufferIndexUnallocated) {
|
|
new_offset += AppendString(shader_object_, "xe_bool_constants");
|
|
constant_name_offset_loop = new_offset;
|
|
new_offset += AppendString(shader_object_, "xe_loop_constants");
|
|
}
|
|
uint32_t constant_name_offset_fetch = new_offset;
|
|
if (constant_name_offset_fetch != kCbufferIndexUnallocated) {
|
|
new_offset += AppendString(shader_object_, "xe_fetch_constants");
|
|
}
|
|
|
|
const uint32_t constant_size = 10 * sizeof(uint32_t);
|
|
|
|
// System constants.
|
|
uint32_t constant_offset_system = new_offset;
|
|
if (cbuffer_index_system_constants_ != kCbufferIndexUnallocated) {
|
|
uint32_t system_cbuffer_constant_offset = 0;
|
|
for (uint32_t i = 0; i < kSysConst_Count; ++i) {
|
|
const SystemConstantRdef& constant = system_constant_rdef_[i];
|
|
shader_object_.push_back(constant_name_offsets_system[i]);
|
|
shader_object_.push_back(system_cbuffer_constant_offset);
|
|
shader_object_.push_back(constant.size);
|
|
// Flag 0x2 is D3D_SVF_USED.
|
|
shader_object_.push_back((system_constants_used_ & (1ull << i)) ? 0x2
|
|
: 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);
|
|
system_cbuffer_constant_offset += constant.size + constant.padding_after;
|
|
new_offset += constant_size;
|
|
}
|
|
}
|
|
|
|
// Float constants.
|
|
uint32_t constant_offset_float = new_offset;
|
|
if (cbuffer_index_float_constants_ != kCbufferIndexUnallocated) {
|
|
shader_object_.push_back(constant_name_offset_float);
|
|
shader_object_.push_back(0);
|
|
shader_object_.push_back(std::max(float_constant_count, 1u) * 4 *
|
|
sizeof(float));
|
|
shader_object_.push_back(0x2);
|
|
shader_object_.push_back(types_offset +
|
|
uint32_t(RdefTypeIndex::kFloat4ConstantArray) *
|
|
type_size);
|
|
shader_object_.push_back(0);
|
|
shader_object_.push_back(0xFFFFFFFFu);
|
|
shader_object_.push_back(0);
|
|
shader_object_.push_back(0xFFFFFFFFu);
|
|
shader_object_.push_back(0);
|
|
new_offset += constant_size;
|
|
}
|
|
|
|
// Bool and loop constants.
|
|
uint32_t constant_offset_bool_loop = new_offset;
|
|
if (cbuffer_index_bool_loop_constants_ != kCbufferIndexUnallocated) {
|
|
shader_object_.push_back(constant_name_offset_bool);
|
|
shader_object_.push_back(0);
|
|
shader_object_.push_back(2 * 4 * sizeof(uint32_t));
|
|
shader_object_.push_back(0x2);
|
|
shader_object_.push_back(types_offset +
|
|
uint32_t(RdefTypeIndex::kUint4Array2) * type_size);
|
|
shader_object_.push_back(0);
|
|
shader_object_.push_back(0xFFFFFFFFu);
|
|
shader_object_.push_back(0);
|
|
shader_object_.push_back(0xFFFFFFFFu);
|
|
shader_object_.push_back(0);
|
|
new_offset += constant_size;
|
|
shader_object_.push_back(constant_name_offset_loop);
|
|
shader_object_.push_back(2 * 4 * sizeof(uint32_t));
|
|
shader_object_.push_back(8 * 4 * sizeof(uint32_t));
|
|
shader_object_.push_back(0x2);
|
|
shader_object_.push_back(types_offset +
|
|
uint32_t(RdefTypeIndex::kUint4Array8) * type_size);
|
|
shader_object_.push_back(0);
|
|
shader_object_.push_back(0xFFFFFFFFu);
|
|
shader_object_.push_back(0);
|
|
shader_object_.push_back(0xFFFFFFFFu);
|
|
shader_object_.push_back(0);
|
|
new_offset += constant_size;
|
|
}
|
|
|
|
// Fetch constants.
|
|
uint32_t constant_offset_fetch = new_offset;
|
|
if (cbuffer_index_fetch_constants_ != kCbufferIndexUnallocated) {
|
|
shader_object_.push_back(constant_name_offset_fetch);
|
|
shader_object_.push_back(0);
|
|
shader_object_.push_back(32 * 6 * sizeof(uint32_t));
|
|
shader_object_.push_back(0x2);
|
|
shader_object_.push_back(
|
|
types_offset + uint32_t(RdefTypeIndex::kUint4Array48) * type_size);
|
|
shader_object_.push_back(0);
|
|
shader_object_.push_back(0xFFFFFFFFu);
|
|
shader_object_.push_back(0);
|
|
shader_object_.push_back(0xFFFFFFFFu);
|
|
shader_object_.push_back(0);
|
|
new_offset += constant_size;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// Constant buffers
|
|
// ***************************************************************************
|
|
|
|
// Write the names.
|
|
new_offset = (uint32_t(shader_object_.size()) - chunk_position_dwords) *
|
|
sizeof(uint32_t);
|
|
uint32_t cbuffer_name_offset_system = new_offset;
|
|
if (cbuffer_index_system_constants_ != kCbufferIndexUnallocated) {
|
|
new_offset += AppendString(shader_object_, "xe_system_cbuffer");
|
|
}
|
|
uint32_t cbuffer_name_offset_float = new_offset;
|
|
if (cbuffer_index_float_constants_ != kCbufferIndexUnallocated) {
|
|
new_offset += AppendString(shader_object_, "xe_float_cbuffer");
|
|
}
|
|
uint32_t cbuffer_name_offset_bool_loop = new_offset;
|
|
if (cbuffer_index_bool_loop_constants_ != kCbufferIndexUnallocated) {
|
|
new_offset += AppendString(shader_object_, "xe_bool_loop_cbuffer");
|
|
}
|
|
uint32_t cbuffer_name_offset_fetch = new_offset;
|
|
if (cbuffer_index_fetch_constants_ != kCbufferIndexUnallocated) {
|
|
new_offset += AppendString(shader_object_, "xe_fetch_cbuffer");
|
|
}
|
|
|
|
// Write the offset to the header.
|
|
shader_object_[chunk_position_dwords + 1] = new_offset;
|
|
|
|
// Write all the constant buffers, sorted by their binding index.
|
|
for (uint32_t i = 0; i < cbuffer_count_; ++i) {
|
|
if (i == cbuffer_index_system_constants_) {
|
|
shader_object_.push_back(cbuffer_name_offset_system);
|
|
shader_object_.push_back(kSysConst_Count);
|
|
shader_object_.push_back(constant_offset_system);
|
|
shader_object_.push_back(
|
|
uint32_t(xe::align(sizeof(SystemConstants), 4 * sizeof(uint32_t))));
|
|
// D3D_CT_CBUFFER.
|
|
shader_object_.push_back(0);
|
|
// No D3D_SHADER_CBUFFER_FLAGS.
|
|
shader_object_.push_back(0);
|
|
} else if (i == cbuffer_index_float_constants_) {
|
|
shader_object_.push_back(cbuffer_name_offset_float);
|
|
shader_object_.push_back(1);
|
|
shader_object_.push_back(constant_offset_float);
|
|
shader_object_.push_back(std::max(float_constant_count, 1u) * 4 *
|
|
sizeof(float));
|
|
shader_object_.push_back(0);
|
|
shader_object_.push_back(0);
|
|
} else if (i == cbuffer_index_bool_loop_constants_) {
|
|
shader_object_.push_back(cbuffer_name_offset_bool_loop);
|
|
// Bool constants and loop constants are separate for easier debugging.
|
|
shader_object_.push_back(2);
|
|
shader_object_.push_back(constant_offset_bool_loop);
|
|
shader_object_.push_back((2 + 8) * 4 * sizeof(uint32_t));
|
|
shader_object_.push_back(0);
|
|
shader_object_.push_back(0);
|
|
} else if (i == cbuffer_index_fetch_constants_) {
|
|
shader_object_.push_back(cbuffer_name_offset_fetch);
|
|
shader_object_.push_back(1);
|
|
shader_object_.push_back(constant_offset_fetch);
|
|
shader_object_.push_back(32 * 6 * sizeof(uint32_t));
|
|
shader_object_.push_back(0);
|
|
shader_object_.push_back(0);
|
|
}
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// Bindings, in s#, t#, u#, 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 sampler_name_offset = 0;
|
|
uint32_t shared_memory_srv_name_offset = 0;
|
|
uint32_t texture_name_offset = 0;
|
|
uint32_t shared_memory_uav_name_offset = 0;
|
|
if (!is_depth_only_pixel_shader_) {
|
|
sampler_name_offset = new_offset;
|
|
for (uint32_t i = 0; i < uint32_t(sampler_bindings_.size()); ++i) {
|
|
new_offset +=
|
|
AppendString(shader_object_, sampler_bindings_[i].name.c_str());
|
|
}
|
|
shared_memory_srv_name_offset = new_offset;
|
|
new_offset += AppendString(shader_object_, "xe_shared_memory_srv");
|
|
texture_name_offset = new_offset;
|
|
for (uint32_t i = 0; i < uint32_t(texture_srvs_.size()); ++i) {
|
|
new_offset += AppendString(shader_object_, texture_srvs_[i].name.c_str());
|
|
}
|
|
shared_memory_uav_name_offset = new_offset;
|
|
new_offset += AppendString(shader_object_, "xe_shared_memory_uav");
|
|
}
|
|
uint32_t edram_name_offset = new_offset;
|
|
if (IsDxbcPixelShader() && edram_rov_used_) {
|
|
new_offset += AppendString(shader_object_, "xe_edram");
|
|
}
|
|
|
|
// Write the offset to the header.
|
|
shader_object_[chunk_position_dwords + 3] = new_offset;
|
|
|
|
if (!is_depth_only_pixel_shader_) {
|
|
// Samplers.
|
|
for (uint32_t i = 0; i < uint32_t(sampler_bindings_.size()); ++i) {
|
|
const SamplerBinding& sampler_binding = sampler_bindings_[i];
|
|
shader_object_.push_back(sampler_name_offset);
|
|
// D3D_SIT_SAMPLER.
|
|
shader_object_.push_back(3);
|
|
// No D3D_RESOURCE_RETURN_TYPE.
|
|
shader_object_.push_back(0);
|
|
// D3D_SRV_DIMENSION_UNKNOWN (not an SRV).
|
|
shader_object_.push_back(0);
|
|
// Multisampling not applicable.
|
|
shader_object_.push_back(0);
|
|
// Register s[i].
|
|
shader_object_.push_back(i);
|
|
// 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);
|
|
// Sampler ID S[i].
|
|
shader_object_.push_back(i);
|
|
sampler_name_offset += GetStringLength(sampler_binding.name.c_str());
|
|
}
|
|
|
|
// Shared memory (when memexport isn't used in the pipeline).
|
|
shader_object_.push_back(shared_memory_srv_name_offset);
|
|
// D3D_SIT_BYTEADDRESS.
|
|
shader_object_.push_back(7);
|
|
// D3D_RETURN_TYPE_MIXED.
|
|
shader_object_.push_back(6);
|
|
// D3D_SRV_DIMENSION_BUFFER.
|
|
shader_object_.push_back(1);
|
|
// Multisampling not applicable.
|
|
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);
|
|
|
|
for (uint32_t i = 0; i < uint32_t(texture_srvs_.size()); ++i) {
|
|
const TextureSRV& texture_srv = texture_srvs_[i];
|
|
shader_object_.push_back(texture_name_offset);
|
|
// D3D_SIT_TEXTURE.
|
|
shader_object_.push_back(2);
|
|
// D3D_RETURN_TYPE_FLOAT.
|
|
shader_object_.push_back(5);
|
|
switch (texture_srv.dimension) {
|
|
case TextureDimension::k3D:
|
|
// D3D_SRV_DIMENSION_TEXTURE3D.
|
|
shader_object_.push_back(8);
|
|
break;
|
|
case TextureDimension::kCube:
|
|
// D3D_SRV_DIMENSION_TEXTURECUBE.
|
|
shader_object_.push_back(9);
|
|
break;
|
|
default:
|
|
// D3D_SRV_DIMENSION_TEXTURE2DARRAY.
|
|
shader_object_.push_back(5);
|
|
}
|
|
// Not multisampled.
|
|
shader_object_.push_back(0xFFFFFFFFu);
|
|
// Register t[1 + i] - t0 is shared memory.
|
|
shader_object_.push_back(1 + i);
|
|
// One binding.
|
|
shader_object_.push_back(1);
|
|
// D3D_SIF_TEXTURE_COMPONENTS (4-component).
|
|
shader_object_.push_back(0xC);
|
|
// Register space 0.
|
|
shader_object_.push_back(0);
|
|
// SRV ID T[1 + i] - T0 is shared memory.
|
|
shader_object_.push_back(1 + i);
|
|
texture_name_offset += GetStringLength(texture_srv.name.c_str());
|
|
}
|
|
|
|
// Shared memory (when memexport is used in the pipeline).
|
|
shader_object_.push_back(shared_memory_uav_name_offset);
|
|
// D3D_SIT_UAV_RWBYTEADDRESS.
|
|
shader_object_.push_back(8);
|
|
// D3D_RETURN_TYPE_MIXED.
|
|
shader_object_.push_back(6);
|
|
// D3D_UAV_DIMENSION_BUFFER.
|
|
shader_object_.push_back(1);
|
|
// Multisampling not applicable.
|
|
shader_object_.push_back(0);
|
|
shader_object_.push_back(uint32_t(UAVRegister::kSharedMemory));
|
|
// 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);
|
|
// UAV ID U0.
|
|
shader_object_.push_back(0);
|
|
}
|
|
|
|
if (IsDxbcPixelShader() && edram_rov_used_) {
|
|
// EDRAM uint32 buffer.
|
|
shader_object_.push_back(edram_name_offset);
|
|
// D3D_SIT_UAV_RWTYPED.
|
|
shader_object_.push_back(4);
|
|
// D3D_RETURN_TYPE_UINT.
|
|
shader_object_.push_back(4);
|
|
// D3D_UAV_DIMENSION_BUFFER.
|
|
shader_object_.push_back(1);
|
|
// Not multisampled.
|
|
shader_object_.push_back(0xFFFFFFFFu);
|
|
shader_object_.push_back(uint32_t(UAVRegister::kEDRAM));
|
|
// 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);
|
|
// UAV ID U1 or U0 depending on whether there's U0.
|
|
shader_object_.push_back(ROV_GetEDRAMUAVIndex());
|
|
}
|
|
|
|
// Constant buffers.
|
|
for (uint32_t i = 0; i < cbuffer_count_; ++i) {
|
|
uint32_t register_index = 0;
|
|
if (i == cbuffer_index_system_constants_) {
|
|
shader_object_.push_back(cbuffer_name_offset_system);
|
|
register_index = uint32_t(CbufferRegister::kSystemConstants);
|
|
} else if (i == cbuffer_index_float_constants_) {
|
|
shader_object_.push_back(cbuffer_name_offset_float);
|
|
register_index = uint32_t(CbufferRegister::kFloatConstants);
|
|
} else if (i == cbuffer_index_bool_loop_constants_) {
|
|
shader_object_.push_back(cbuffer_name_offset_bool_loop);
|
|
register_index = uint32_t(CbufferRegister::kBoolLoopConstants);
|
|
} else if (i == cbuffer_index_fetch_constants_) {
|
|
shader_object_.push_back(cbuffer_name_offset_fetch);
|
|
register_index = uint32_t(CbufferRegister::kFetchConstants);
|
|
}
|
|
// 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);
|
|
// Multisampling not applicable.
|
|
shader_object_.push_back(0);
|
|
shader_object_.push_back(register_index);
|
|
// One binding.
|
|
shader_object_.push_back(1);
|
|
// D3D_SIF_USERPACKED if a `cbuffer` rather than a `ConstantBuffer<T>`, but
|
|
// we don't use indexable constant buffer descriptors.
|
|
shader_object_.push_back(0);
|
|
// Register space 0.
|
|
shader_object_.push_back(0);
|
|
// CBV ID CB[i].
|
|
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 (IsDxbcVertexShader()) {
|
|
// 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(uint32_t(InOutRegister::kVSInVertexIndex));
|
|
// x present, x used (always written to GPR 0).
|
|
shader_object_.push_back(0b0001 | (0b0001 << 8));
|
|
|
|
// Vertex index semantic name.
|
|
AppendString(shader_object_, "SV_VertexID");
|
|
} else if (IsDxbcDomainShader()) {
|
|
// No inputs - tessellation factors specified in PCSG.
|
|
shader_object_.push_back(0);
|
|
// Unknown.
|
|
shader_object_.push_back(8);
|
|
} else {
|
|
assert_true(IsDxbcPixelShader());
|
|
// Interpolators, point parameters (coordinates, size), clip space ZW,
|
|
// screen position, is front face.
|
|
shader_object_.push_back(kInterpolatorCount + 4);
|
|
// 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(uint32_t(InOutRegister::kPSInInterpolators) + i);
|
|
// Interpolators are copied to GPRs in the beginning of the shader. If
|
|
// there's a register to copy to, this interpolator is used.
|
|
uint32_t interpolator_used =
|
|
(!is_depth_only_pixel_shader_ && i < register_count()) ? (0b1111 << 8)
|
|
: 0b0000;
|
|
shader_object_.push_back(0b1111 | interpolator_used);
|
|
}
|
|
|
|
// 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(uint32_t(InOutRegister::kPSInPointParameters));
|
|
shader_object_.push_back(
|
|
0b0111 | (is_depth_only_pixel_shader_ ? 0b0000 : (0b0011 << 8)));
|
|
|
|
// Z and W in clip space, for getting per-sample depth with ROV.
|
|
shader_object_.push_back(0);
|
|
shader_object_.push_back(kClipSpaceZWTexCoord);
|
|
shader_object_.push_back(0);
|
|
shader_object_.push_back(3);
|
|
shader_object_.push_back(uint32_t(InOutRegister::kPSInClipSpaceZW));
|
|
shader_object_.push_back(0b0011 |
|
|
((edram_rov_used_ ? 0b0011 : 0b0000) << 8));
|
|
|
|
// Position (only XY needed for ps_param_gen and for EDRAM address
|
|
// calculation). Z is not needed - ROV depth testing calculates the depth
|
|
// from the clip space Z/W texcoord, and if oDepth is used, it must be
|
|
// written to on every execution path anyway.
|
|
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(uint32_t(InOutRegister::kPSInPosition));
|
|
shader_object_.push_back(0b1111 | (0b0011 << 8));
|
|
|
|
// Is front face. Always used because ps_param_gen is handled dynamically.
|
|
shader_object_.push_back(0);
|
|
shader_object_.push_back(0);
|
|
// D3D_NAME_IS_FRONT_FACE.
|
|
shader_object_.push_back(9);
|
|
shader_object_.push_back(1);
|
|
shader_object_.push_back(uint32_t(InOutRegister::kPSInFrontFace));
|
|
if (edram_rov_used_) {
|
|
shader_object_.push_back(0b0001 | (0b0001 << 8));
|
|
} else {
|
|
shader_object_.push_back(
|
|
0b0001 | (is_depth_only_pixel_shader_ ? 0b0000 : (0b0001 << 8)));
|
|
}
|
|
|
|
// Write the semantic names.
|
|
new_offset = (uint32_t(shader_object_.size()) - chunk_position_dwords) *
|
|
sizeof(uint32_t);
|
|
uint32_t name_position_dwords =
|
|
chunk_position_dwords + signature_position_dwords;
|
|
for (uint32_t i = 0; i < kInterpolatorCount + 2; ++i) {
|
|
shader_object_[name_position_dwords] = new_offset;
|
|
name_position_dwords += signature_size_dwords;
|
|
}
|
|
new_offset += AppendString(shader_object_, "TEXCOORD");
|
|
shader_object_[name_position_dwords] = new_offset;
|
|
name_position_dwords += signature_size_dwords;
|
|
new_offset += AppendString(shader_object_, "SV_Position");
|
|
shader_object_[name_position_dwords] = new_offset;
|
|
name_position_dwords += signature_size_dwords;
|
|
new_offset += AppendString(shader_object_, "SV_IsFrontFace");
|
|
}
|
|
}
|
|
|
|
void DxbcShaderTranslator::WritePatchConstantSignature() {
|
|
assert_true(IsDxbcDomainShader());
|
|
|
|
uint32_t chunk_position_dwords = uint32_t(shader_object_.size());
|
|
|
|
const uint32_t signature_position_dwords = 2;
|
|
const uint32_t signature_size_dwords = 6;
|
|
|
|
// FXC refuses to compile without SV_TessFactor and SV_InsideTessFactor input,
|
|
// so this is required.
|
|
uint32_t tess_factor_count_edge, tess_factor_count_inside;
|
|
if (patch_primitive_type() == PrimitiveType::kTrianglePatch) {
|
|
tess_factor_count_edge = 3;
|
|
tess_factor_count_inside = 1;
|
|
} else {
|
|
// TODO(Triang3l): Support line patches.
|
|
assert_true(patch_primitive_type() == PrimitiveType::kQuadPatch);
|
|
tess_factor_count_edge = 4;
|
|
tess_factor_count_inside = 2;
|
|
}
|
|
uint32_t tess_factor_count_total =
|
|
tess_factor_count_edge + tess_factor_count_inside;
|
|
shader_object_.push_back(tess_factor_count_total);
|
|
// Unknown.
|
|
shader_object_.push_back(8);
|
|
|
|
for (uint32_t i = 0; i < tess_factor_count_total; ++i) {
|
|
// Reserve space for the semantic name (SV_TessFactor or
|
|
// SV_InsideTessFactor).
|
|
shader_object_.push_back(0);
|
|
shader_object_.push_back(
|
|
i < tess_factor_count_edge ? i : (i - tess_factor_count_edge));
|
|
if (patch_primitive_type() == PrimitiveType::kTrianglePatch) {
|
|
if (i < tess_factor_count_edge) {
|
|
// D3D_NAME_FINAL_TRI_EDGE_TESSFACTOR.
|
|
shader_object_.push_back(13);
|
|
} else {
|
|
// D3D_NAME_FINAL_TRI_INSIDE_TESSFACTOR.
|
|
shader_object_.push_back(14);
|
|
}
|
|
} else {
|
|
// TODO(Triang3l): Support line patches.
|
|
assert_true(patch_primitive_type() == PrimitiveType::kQuadPatch);
|
|
if (i < tess_factor_count_edge) {
|
|
// D3D_NAME_FINAL_QUAD_EDGE_TESSFACTOR.
|
|
shader_object_.push_back(11);
|
|
} else {
|
|
// D3D_NAME_FINAL_QUAD_INSIDE_TESSFACTOR.
|
|
shader_object_.push_back(12);
|
|
}
|
|
}
|
|
// D3D_REGISTER_COMPONENT_FLOAT32.
|
|
shader_object_.push_back(3);
|
|
// Not using any of these, and just assigning consecutive registers.
|
|
shader_object_.push_back(i);
|
|
// 1 component, none used.
|
|
shader_object_.push_back(1);
|
|
}
|
|
|
|
// Write the semantic names.
|
|
uint32_t new_offset =
|
|
(uint32_t(shader_object_.size()) - chunk_position_dwords) *
|
|
sizeof(uint32_t);
|
|
uint32_t name_position_dwords =
|
|
chunk_position_dwords + signature_position_dwords;
|
|
for (uint32_t i = 0; i < tess_factor_count_edge; ++i) {
|
|
shader_object_[name_position_dwords] = new_offset;
|
|
name_position_dwords += signature_size_dwords;
|
|
}
|
|
new_offset += AppendString(shader_object_, "SV_TessFactor");
|
|
for (uint32_t i = 0; i < tess_factor_count_inside; ++i) {
|
|
shader_object_[name_position_dwords] = new_offset;
|
|
name_position_dwords += signature_size_dwords;
|
|
}
|
|
new_offset += AppendString(shader_object_, "SV_InsideTessFactor");
|
|
}
|
|
|
|
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 (IsDxbcVertexOrDomainShader()) {
|
|
// Interpolators, point parameters (coordinates, size), clip space ZW,
|
|
// screen position, 6 clip distances in 2 vectors.
|
|
shader_object_.push_back(kInterpolatorCount + 5);
|
|
// 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(uint32_t(InOutRegister::kVSOutInterpolators) +
|
|
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(0b1111);
|
|
}
|
|
|
|
// 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(uint32_t(InOutRegister::kVSOutPointParameters));
|
|
shader_object_.push_back(0b0111 | (0b1000 << 8));
|
|
|
|
// Z and W in clip space, for getting per-sample depth with ROV.
|
|
shader_object_.push_back(0);
|
|
shader_object_.push_back(kClipSpaceZWTexCoord);
|
|
shader_object_.push_back(0);
|
|
shader_object_.push_back(3);
|
|
shader_object_.push_back(uint32_t(InOutRegister::kVSOutClipSpaceZW));
|
|
shader_object_.push_back(0b0011 | (0b1100 << 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(uint32_t(InOutRegister::kVSOutPosition));
|
|
shader_object_.push_back(0b1111);
|
|
|
|
// Clip distances.
|
|
for (uint32_t i = 0; i < 2; ++i) {
|
|
shader_object_.push_back(0);
|
|
shader_object_.push_back(i);
|
|
// D3D_NAME_CLIP_DISTANCE.
|
|
shader_object_.push_back(2);
|
|
shader_object_.push_back(3);
|
|
shader_object_.push_back(uint32_t(InOutRegister::kVSOutClipDistance0123) +
|
|
i);
|
|
shader_object_.push_back(i ? (0b0011 | (0b1100 << 8)) : 0b1111);
|
|
}
|
|
|
|
// Write the semantic names.
|
|
new_offset = (uint32_t(shader_object_.size()) - chunk_position_dwords) *
|
|
sizeof(uint32_t);
|
|
uint32_t name_position_dwords =
|
|
chunk_position_dwords + signature_position_dwords;
|
|
for (uint32_t i = 0; i < kInterpolatorCount + 2; ++i) {
|
|
shader_object_[name_position_dwords] = new_offset;
|
|
name_position_dwords += signature_size_dwords;
|
|
}
|
|
new_offset += AppendString(shader_object_, "TEXCOORD");
|
|
shader_object_[name_position_dwords] = new_offset;
|
|
name_position_dwords += signature_size_dwords;
|
|
new_offset += AppendString(shader_object_, "SV_Position");
|
|
for (uint32_t i = 0; i < 2; ++i) {
|
|
shader_object_[name_position_dwords] = new_offset;
|
|
name_position_dwords += signature_size_dwords;
|
|
}
|
|
new_offset += AppendString(shader_object_, "SV_ClipDistance");
|
|
} else {
|
|
assert_true(IsDxbcPixelShader());
|
|
if (edram_rov_used_) {
|
|
// No outputs - only ROV read/write.
|
|
shader_object_.push_back(0);
|
|
// Unknown.
|
|
shader_object_.push_back(8);
|
|
} else {
|
|
bool writes_color = writes_any_color_target();
|
|
// Color render targets, optionally depth.
|
|
shader_object_.push_back((writes_color ? 4 : 0) +
|
|
(writes_depth() ? 1 : 0));
|
|
// Unknown.
|
|
shader_object_.push_back(8);
|
|
|
|
// Color render targets.
|
|
if (writes_color) {
|
|
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);
|
|
uint32_t name_position_dwords =
|
|
chunk_position_dwords + signature_position_dwords;
|
|
if (writes_color) {
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
shader_object_[name_position_dwords] = new_offset;
|
|
name_position_dwords += signature_size_dwords;
|
|
}
|
|
}
|
|
new_offset += AppendString(shader_object_, "SV_Target");
|
|
if (writes_depth()) {
|
|
shader_object_[name_position_dwords] = new_offset;
|
|
name_position_dwords += signature_size_dwords;
|
|
new_offset += AppendString(shader_object_, "SV_Depth");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void DxbcShaderTranslator::WriteShaderCode() {
|
|
uint32_t chunk_position_dwords = uint32_t(shader_object_.size());
|
|
|
|
uint32_t shader_type;
|
|
if (IsDxbcVertexShader()) {
|
|
shader_type = D3D10_SB_VERTEX_SHADER;
|
|
} else if (IsDxbcDomainShader()) {
|
|
shader_type = D3D11_SB_DOMAIN_SHADER;
|
|
} else {
|
|
assert_true(IsDxbcPixelShader());
|
|
shader_type = D3D10_SB_PIXEL_SHADER;
|
|
}
|
|
shader_object_.push_back(
|
|
ENCODE_D3D10_SB_TOKENIZED_PROGRAM_VERSION_TOKEN(shader_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. Also dcl_ instructions for bindings are followed by the register
|
|
// space index.
|
|
//
|
|
// Inputs/outputs have 1D-indexed operands with a component mask and a
|
|
// register index.
|
|
|
|
if (IsDxbcDomainShader()) {
|
|
// Not using control point data since Xenos only has a vertex shader acting
|
|
// as both vertex shader and domain shader.
|
|
uint32_t control_point_count;
|
|
D3D11_SB_TESSELLATOR_DOMAIN domain;
|
|
if (patch_primitive_type() == PrimitiveType::kTrianglePatch) {
|
|
control_point_count = 3;
|
|
domain = D3D11_SB_TESSELLATOR_DOMAIN_TRI;
|
|
} else {
|
|
// TODO(Triang3l): Support line patches.
|
|
assert_true(patch_primitive_type() == PrimitiveType::kQuadPatch);
|
|
control_point_count = 4;
|
|
domain = D3D11_SB_TESSELLATOR_DOMAIN_QUAD;
|
|
}
|
|
shader_object_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(
|
|
D3D11_SB_OPCODE_DCL_INPUT_CONTROL_POINT_COUNT) |
|
|
ENCODE_D3D11_SB_INPUT_CONTROL_POINT_COUNT(control_point_count) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1));
|
|
stat_.c_control_points = control_point_count;
|
|
shader_object_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D11_SB_OPCODE_DCL_TESS_DOMAIN) |
|
|
ENCODE_D3D11_SB_TESS_DOMAIN(domain) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1));
|
|
}
|
|
|
|
// Don't allow refactoring when converting to native code to maintain position
|
|
// invariance (needed even in pixel shaders for oDepth invariance). Also this
|
|
// dcl will be modified by ForceEarlyDepthStencil.
|
|
shader_object_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_GLOBAL_FLAGS) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(1));
|
|
|
|
// Constant buffers, from most frequenly accessed to least frequently accessed
|
|
// (the order is a hint to the driver according to the DXBC header).
|
|
if (cbuffer_index_float_constants_ != kCbufferIndexUnallocated) {
|
|
uint32_t float_constant_count = 0;
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
float_constant_count +=
|
|
xe::bit_count(constant_register_map().float_bitmap[i]);
|
|
}
|
|
shader_object_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_CONSTANT_BUFFER) |
|
|
ENCODE_D3D10_SB_D3D10_SB_CONSTANT_BUFFER_ACCESS_PATTERN(
|
|
float_constants_dynamic_indexed_
|
|
? D3D10_SB_CONSTANT_BUFFER_DYNAMIC_INDEXED
|
|
: D3D10_SB_CONSTANT_BUFFER_IMMEDIATE_INDEXED) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7));
|
|
shader_object_.push_back(EncodeVectorSwizzledOperand(
|
|
D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSwizzleXYZW, 3));
|
|
shader_object_.push_back(cbuffer_index_float_constants_);
|
|
shader_object_.push_back(uint32_t(CbufferRegister::kFloatConstants));
|
|
shader_object_.push_back(uint32_t(CbufferRegister::kFloatConstants));
|
|
shader_object_.push_back(float_constant_count);
|
|
shader_object_.push_back(0);
|
|
}
|
|
if (cbuffer_index_system_constants_ != kCbufferIndexUnallocated) {
|
|
shader_object_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_CONSTANT_BUFFER) |
|
|
ENCODE_D3D10_SB_D3D10_SB_CONSTANT_BUFFER_ACCESS_PATTERN(
|
|
D3D10_SB_CONSTANT_BUFFER_IMMEDIATE_INDEXED) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7));
|
|
shader_object_.push_back(EncodeVectorSwizzledOperand(
|
|
D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSwizzleXYZW, 3));
|
|
shader_object_.push_back(cbuffer_index_system_constants_);
|
|
shader_object_.push_back(uint32_t(CbufferRegister::kSystemConstants));
|
|
shader_object_.push_back(uint32_t(CbufferRegister::kSystemConstants));
|
|
shader_object_.push_back((sizeof(SystemConstants) + 15) >> 4);
|
|
shader_object_.push_back(0);
|
|
}
|
|
if (cbuffer_index_fetch_constants_ != kCbufferIndexUnallocated) {
|
|
shader_object_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_CONSTANT_BUFFER) |
|
|
ENCODE_D3D10_SB_D3D10_SB_CONSTANT_BUFFER_ACCESS_PATTERN(
|
|
D3D10_SB_CONSTANT_BUFFER_IMMEDIATE_INDEXED) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7));
|
|
shader_object_.push_back(EncodeVectorSwizzledOperand(
|
|
D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSwizzleXYZW, 3));
|
|
shader_object_.push_back(cbuffer_index_fetch_constants_);
|
|
shader_object_.push_back(uint32_t(CbufferRegister::kFetchConstants));
|
|
shader_object_.push_back(uint32_t(CbufferRegister::kFetchConstants));
|
|
shader_object_.push_back(48);
|
|
shader_object_.push_back(0);
|
|
}
|
|
if (cbuffer_index_bool_loop_constants_ != kCbufferIndexUnallocated) {
|
|
shader_object_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_CONSTANT_BUFFER) |
|
|
ENCODE_D3D10_SB_D3D10_SB_CONSTANT_BUFFER_ACCESS_PATTERN(
|
|
D3D10_SB_CONSTANT_BUFFER_IMMEDIATE_INDEXED) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7));
|
|
shader_object_.push_back(EncodeVectorSwizzledOperand(
|
|
D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER, kSwizzleXYZW, 3));
|
|
shader_object_.push_back(cbuffer_index_bool_loop_constants_);
|
|
shader_object_.push_back(uint32_t(CbufferRegister::kBoolLoopConstants));
|
|
shader_object_.push_back(uint32_t(CbufferRegister::kBoolLoopConstants));
|
|
shader_object_.push_back(10);
|
|
shader_object_.push_back(0);
|
|
}
|
|
|
|
if (!is_depth_only_pixel_shader_) {
|
|
// Samplers.
|
|
for (uint32_t i = 0; i < uint32_t(sampler_bindings_.size()); ++i) {
|
|
const SamplerBinding& sampler_binding = sampler_bindings_[i];
|
|
shader_object_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_SAMPLER) |
|
|
ENCODE_D3D10_SB_SAMPLER_MODE(D3D10_SB_SAMPLER_MODE_DEFAULT) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(6));
|
|
shader_object_.push_back(EncodeVectorSwizzledOperand(
|
|
D3D10_SB_OPERAND_TYPE_SAMPLER, kSwizzleXYZW, 3));
|
|
shader_object_.push_back(i);
|
|
shader_object_.push_back(i);
|
|
shader_object_.push_back(i);
|
|
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(EncodeVectorSwizzledOperand(
|
|
D3D10_SB_OPERAND_TYPE_RESOURCE, kSwizzleXYZW, 3));
|
|
shader_object_.push_back(0);
|
|
shader_object_.push_back(0);
|
|
shader_object_.push_back(0);
|
|
shader_object_.push_back(0);
|
|
// Textures.
|
|
for (uint32_t i = 0; i < uint32_t(texture_srvs_.size()); ++i) {
|
|
const TextureSRV& texture_srv = texture_srvs_[i];
|
|
D3D10_SB_RESOURCE_DIMENSION texture_srv_dimension;
|
|
switch (texture_srv.dimension) {
|
|
case TextureDimension::k3D:
|
|
texture_srv_dimension = D3D10_SB_RESOURCE_DIMENSION_TEXTURE3D;
|
|
break;
|
|
case TextureDimension::kCube:
|
|
texture_srv_dimension = D3D10_SB_RESOURCE_DIMENSION_TEXTURECUBE;
|
|
break;
|
|
default:
|
|
texture_srv_dimension = D3D10_SB_RESOURCE_DIMENSION_TEXTURE2DARRAY;
|
|
}
|
|
shader_object_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_RESOURCE) |
|
|
ENCODE_D3D10_SB_RESOURCE_DIMENSION(texture_srv_dimension) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7));
|
|
shader_object_.push_back(EncodeVectorSwizzledOperand(
|
|
D3D10_SB_OPERAND_TYPE_RESOURCE, kSwizzleXYZW, 3));
|
|
// T0 is shared memory.
|
|
shader_object_.push_back(1 + i);
|
|
// t0 is shared memory.
|
|
shader_object_.push_back(1 + i);
|
|
shader_object_.push_back(1 + i);
|
|
shader_object_.push_back(
|
|
ENCODE_D3D10_SB_RESOURCE_RETURN_TYPE(D3D10_SB_RETURN_TYPE_FLOAT, 0) |
|
|
ENCODE_D3D10_SB_RESOURCE_RETURN_TYPE(D3D10_SB_RETURN_TYPE_FLOAT, 1) |
|
|
ENCODE_D3D10_SB_RESOURCE_RETURN_TYPE(D3D10_SB_RETURN_TYPE_FLOAT, 2) |
|
|
ENCODE_D3D10_SB_RESOURCE_RETURN_TYPE(D3D10_SB_RETURN_TYPE_FLOAT, 3));
|
|
shader_object_.push_back(0);
|
|
}
|
|
}
|
|
|
|
// Unordered access views.
|
|
if (!is_depth_only_pixel_shader_) {
|
|
// Shared memory RWByteAddressBuffer.
|
|
shader_object_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(
|
|
D3D11_SB_OPCODE_DCL_UNORDERED_ACCESS_VIEW_RAW) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(6));
|
|
shader_object_.push_back(EncodeVectorSwizzledOperand(
|
|
D3D11_SB_OPERAND_TYPE_UNORDERED_ACCESS_VIEW, kSwizzleXYZW, 3));
|
|
shader_object_.push_back(0);
|
|
shader_object_.push_back(uint32_t(UAVRegister::kSharedMemory));
|
|
shader_object_.push_back(uint32_t(UAVRegister::kSharedMemory));
|
|
shader_object_.push_back(0);
|
|
}
|
|
if (IsDxbcPixelShader() && edram_rov_used_) {
|
|
// EDRAM uint32 rasterizer-ordered buffer.
|
|
shader_object_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(
|
|
D3D11_SB_OPCODE_DCL_UNORDERED_ACCESS_VIEW_TYPED) |
|
|
ENCODE_D3D10_SB_RESOURCE_DIMENSION(D3D10_SB_RESOURCE_DIMENSION_BUFFER) |
|
|
D3D11_SB_RASTERIZER_ORDERED_ACCESS |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(7));
|
|
shader_object_.push_back(EncodeVectorSwizzledOperand(
|
|
D3D11_SB_OPERAND_TYPE_UNORDERED_ACCESS_VIEW, kSwizzleXYZW, 3));
|
|
shader_object_.push_back(ROV_GetEDRAMUAVIndex());
|
|
shader_object_.push_back(uint32_t(UAVRegister::kEDRAM));
|
|
shader_object_.push_back(uint32_t(UAVRegister::kEDRAM));
|
|
shader_object_.push_back(
|
|
ENCODE_D3D10_SB_RESOURCE_RETURN_TYPE(D3D10_SB_RETURN_TYPE_UINT, 0) |
|
|
ENCODE_D3D10_SB_RESOURCE_RETURN_TYPE(D3D10_SB_RETURN_TYPE_UINT, 1) |
|
|
ENCODE_D3D10_SB_RESOURCE_RETURN_TYPE(D3D10_SB_RETURN_TYPE_UINT, 2) |
|
|
ENCODE_D3D10_SB_RESOURCE_RETURN_TYPE(D3D10_SB_RETURN_TYPE_UINT, 3));
|
|
shader_object_.push_back(0);
|
|
}
|
|
|
|
// Inputs and outputs.
|
|
if (IsDxbcVertexOrDomainShader()) {
|
|
if (IsDxbcDomainShader()) {
|
|
// Domain location input (barycentric for triangles, UV for quads).
|
|
uint32_t domain_location_mask;
|
|
if (patch_primitive_type() == PrimitiveType::kTrianglePatch) {
|
|
domain_location_mask = 0b0111;
|
|
} else {
|
|
// TODO(Triang3l): Support line patches.
|
|
assert_true(patch_primitive_type() == PrimitiveType::kQuadPatch);
|
|
domain_location_mask = 0b0011;
|
|
}
|
|
shader_object_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_INPUT) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(2));
|
|
shader_object_.push_back(EncodeVectorMaskedOperand(
|
|
D3D11_SB_OPERAND_TYPE_INPUT_DOMAIN_POINT, domain_location_mask, 0));
|
|
++stat_.dcl_count;
|
|
// Primitive index input.
|
|
shader_object_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_INPUT) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(2));
|
|
shader_object_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_INPUT_PRIMITIVEID, 0));
|
|
++stat_.dcl_count;
|
|
} else {
|
|
// 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(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_INPUT, 0b0001, 1));
|
|
shader_object_.push_back(uint32_t(InOutRegister::kVSInVertexIndex));
|
|
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(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_OUTPUT, 0b1111, 1));
|
|
shader_object_.push_back(uint32_t(InOutRegister::kVSOutInterpolators) +
|
|
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(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_OUTPUT, 0b0111, 1));
|
|
shader_object_.push_back(uint32_t(InOutRegister::kVSOutPointParameters));
|
|
++stat_.dcl_count;
|
|
// Clip space Z and W 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(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_OUTPUT, 0b0011, 1));
|
|
shader_object_.push_back(uint32_t(InOutRegister::kVSOutClipSpaceZW));
|
|
++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(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_OUTPUT, 0b1111, 1));
|
|
shader_object_.push_back(uint32_t(InOutRegister::kVSOutPosition));
|
|
shader_object_.push_back(ENCODE_D3D10_SB_NAME(D3D10_SB_NAME_POSITION));
|
|
++stat_.dcl_count;
|
|
// Clip distance outputs.
|
|
for (uint32_t i = 0; i < 2; ++i) {
|
|
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(EncodeVectorMaskedOperand(
|
|
D3D10_SB_OPERAND_TYPE_OUTPUT, i ? 0b0011 : 0b1111, 1));
|
|
shader_object_.push_back(uint32_t(InOutRegister::kVSOutClipDistance0123) +
|
|
i);
|
|
shader_object_.push_back(
|
|
ENCODE_D3D10_SB_NAME(D3D10_SB_NAME_CLIP_DISTANCE));
|
|
++stat_.dcl_count;
|
|
}
|
|
} else if (IsDxbcPixelShader()) {
|
|
// Interpolator input.
|
|
if (!is_depth_only_pixel_shader_) {
|
|
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_INPUT_INTERPOLATION_MODE(
|
|
D3D10_SB_INTERPOLATION_LINEAR) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3));
|
|
shader_object_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_INPUT, 0b1111, 1));
|
|
shader_object_.push_back(uint32_t(InOutRegister::kPSInInterpolators) +
|
|
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_INPUT_INTERPOLATION_MODE(
|
|
D3D10_SB_INTERPOLATION_LINEAR) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3));
|
|
shader_object_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_INPUT, 0b0011, 1));
|
|
shader_object_.push_back(uint32_t(InOutRegister::kPSInPointParameters));
|
|
++stat_.dcl_count;
|
|
}
|
|
if (edram_rov_used_) {
|
|
// Z and W in clip space, for per-sample depth.
|
|
shader_object_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_INPUT_PS) |
|
|
ENCODE_D3D10_SB_INPUT_INTERPOLATION_MODE(
|
|
D3D10_SB_INTERPOLATION_LINEAR) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(3));
|
|
shader_object_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_INPUT, 0b0011, 1));
|
|
shader_object_.push_back(uint32_t(InOutRegister::kPSInClipSpaceZW));
|
|
++stat_.dcl_count;
|
|
}
|
|
// Position input (only XY needed for ps_param_gen, and the ROV depth code
|
|
// calculates the depth from clip space Z and W).
|
|
shader_object_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_INPUT_PS_SIV) |
|
|
ENCODE_D3D10_SB_INPUT_INTERPOLATION_MODE(
|
|
D3D10_SB_INTERPOLATION_LINEAR_NOPERSPECTIVE) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(4));
|
|
shader_object_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_INPUT, 0b0011, 1));
|
|
shader_object_.push_back(uint32_t(InOutRegister::kPSInPosition));
|
|
shader_object_.push_back(ENCODE_D3D10_SB_NAME(D3D10_SB_NAME_POSITION));
|
|
++stat_.dcl_count;
|
|
if (edram_rov_used_ || !is_depth_only_pixel_shader_) {
|
|
// Is front face.
|
|
shader_object_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_INPUT_PS_SGV) |
|
|
// This needs to be set according to FXC output, despite the
|
|
// description in d3d12TokenizedProgramFormat.hpp saying bits 11:23
|
|
// are ignored.
|
|
ENCODE_D3D10_SB_INPUT_INTERPOLATION_MODE(
|
|
D3D10_SB_INTERPOLATION_CONSTANT) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(4));
|
|
shader_object_.push_back(
|
|
EncodeVectorMaskedOperand(D3D10_SB_OPERAND_TYPE_INPUT, 0b0001, 1));
|
|
shader_object_.push_back(uint32_t(InOutRegister::kPSInFrontFace));
|
|
shader_object_.push_back(
|
|
ENCODE_D3D10_SB_NAME(D3D10_SB_NAME_IS_FRONT_FACE));
|
|
++stat_.dcl_count;
|
|
}
|
|
if (edram_rov_used_) {
|
|
// Sample coverage input.
|
|
shader_object_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_INPUT) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(2));
|
|
shader_object_.push_back(
|
|
EncodeScalarOperand(D3D11_SB_OPERAND_TYPE_INPUT_COVERAGE_MASK, 0));
|
|
++stat_.dcl_count;
|
|
} else {
|
|
if (writes_any_color_target()) {
|
|
// Color output.
|
|
for (uint32_t i = 0; i < 4; ++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(EncodeVectorMaskedOperand(
|
|
D3D10_SB_OPERAND_TYPE_OUTPUT, 0b1111, 1));
|
|
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(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_OUTPUT_DEPTH, 0));
|
|
++stat_.dcl_count;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Temporary registers - guest general-purpose registers if not using dynamic
|
|
// indexing and Xenia internal registers.
|
|
stat_.temp_register_count = system_temp_count_max_;
|
|
if (!is_depth_only_pixel_shader_ && !uses_register_dynamic_addressing()) {
|
|
stat_.temp_register_count += register_count();
|
|
}
|
|
if (stat_.temp_register_count != 0) {
|
|
shader_object_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_TEMPS) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(2));
|
|
shader_object_.push_back(stat_.temp_register_count);
|
|
}
|
|
|
|
// General-purpose registers if using dynamic indexing (x0).
|
|
if (!is_depth_only_pixel_shader_ && uses_register_dynamic_addressing()) {
|
|
assert_true(register_count() != 0);
|
|
shader_object_.push_back(
|
|
ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_DCL_INDEXABLE_TEMP) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(4));
|
|
// x0.
|
|
shader_object_.push_back(0);
|
|
shader_object_.push_back(register_count());
|
|
// 4 components in each.
|
|
shader_object_.push_back(4);
|
|
stat_.temp_array_count += register_count();
|
|
}
|
|
|
|
// Initialize the depth output if used, which must be initialized on every
|
|
// execution path.
|
|
if (!edram_rov_used_ && IsDxbcPixelShader() && writes_depth()) {
|
|
shader_object_.push_back(ENCODE_D3D10_SB_OPCODE_TYPE(D3D10_SB_OPCODE_MOV) |
|
|
ENCODE_D3D10_SB_TOKENIZED_INSTRUCTION_LENGTH(4));
|
|
shader_object_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_OUTPUT_DEPTH, 0));
|
|
shader_object_.push_back(
|
|
EncodeScalarOperand(D3D10_SB_OPERAND_TYPE_IMMEDIATE32, 0));
|
|
shader_object_.push_back(0);
|
|
++stat_.instruction_count;
|
|
++stat_.mov_instruction_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
|