1216 lines
47 KiB
C++
1216 lines
47 KiB
C++
/**
|
|
******************************************************************************
|
|
* Xenia : Xbox 360 Emulator Research Project *
|
|
******************************************************************************
|
|
* Copyright 2022 Ben Vanik. All rights reserved. *
|
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
|
******************************************************************************
|
|
*/
|
|
|
|
#include "xenia/gpu/shader_interpreter.h"
|
|
|
|
#include <cfloat>
|
|
#include <cmath>
|
|
#include <cstring>
|
|
|
|
#include "xenia/base/assert.h"
|
|
#include "xenia/base/byte_order.h"
|
|
#include "xenia/base/math.h"
|
|
#include "xenia/gpu/registers.h"
|
|
#include "xenia/gpu/trace_writer.h"
|
|
#include "xenia/gpu/xenos.h"
|
|
|
|
namespace xe {
|
|
namespace gpu {
|
|
|
|
void ShaderInterpreter::Execute() {
|
|
// For more consistency between invocations in case of a malformed shader.
|
|
state_.Reset();
|
|
|
|
const uint32_t* bool_constants =
|
|
®ister_file_[XE_GPU_REG_SHADER_CONSTANT_BOOL_000_031].u32;
|
|
const xenos::LoopConstant* loop_constants =
|
|
reinterpret_cast<const xenos::LoopConstant*>(
|
|
®ister_file_[XE_GPU_REG_SHADER_CONSTANT_LOOP_00].u32);
|
|
|
|
bool exec_ended = false;
|
|
uint32_t cf_index_next = 1;
|
|
for (uint32_t cf_index = 0; !exec_ended; cf_index = cf_index_next) {
|
|
cf_index_next = cf_index + 1;
|
|
|
|
const uint32_t* cf_pair = &ucode_[3 * (cf_index >> 1)];
|
|
ucode::ControlFlowInstruction cf_instr;
|
|
if (cf_index & 1) {
|
|
cf_instr.dword_0 = (cf_pair[1] >> 16) | (cf_pair[2] << 16);
|
|
cf_instr.dword_1 = cf_pair[2] >> 16;
|
|
} else {
|
|
cf_instr.dword_0 = cf_pair[0];
|
|
cf_instr.dword_1 = cf_pair[1] & 0xFFFF;
|
|
}
|
|
|
|
ucode::ControlFlowOpcode cf_opcode = cf_instr.opcode();
|
|
switch (cf_opcode) {
|
|
case ucode::ControlFlowOpcode::kNop: {
|
|
} break;
|
|
|
|
case ucode::ControlFlowOpcode::kExec:
|
|
case ucode::ControlFlowOpcode::kExecEnd:
|
|
case ucode::ControlFlowOpcode::kCondExec:
|
|
case ucode::ControlFlowOpcode::kCondExecEnd:
|
|
case ucode::ControlFlowOpcode::kCondExecPred:
|
|
case ucode::ControlFlowOpcode::kCondExecPredEnd:
|
|
case ucode::ControlFlowOpcode::kCondExecPredClean:
|
|
case ucode::ControlFlowOpcode::kCondExecPredCleanEnd: {
|
|
ucode::ControlFlowExecInstruction cf_exec =
|
|
*reinterpret_cast<const ucode::ControlFlowExecInstruction*>(
|
|
&cf_instr);
|
|
|
|
switch (cf_opcode) {
|
|
case ucode::ControlFlowOpcode::kCondExec:
|
|
case ucode::ControlFlowOpcode::kCondExecEnd:
|
|
case ucode::ControlFlowOpcode::kCondExecPredClean:
|
|
case ucode::ControlFlowOpcode::kCondExecPredCleanEnd: {
|
|
const ucode::ControlFlowCondExecInstruction cf_cond_exec =
|
|
*reinterpret_cast<const ucode::ControlFlowCondExecInstruction*>(
|
|
&cf_exec);
|
|
uint32_t bool_address = cf_cond_exec.bool_address();
|
|
if (cf_cond_exec.condition() !=
|
|
((bool_constants[bool_address >> 5] &
|
|
(UINT32_C(1) << (bool_address & 31))) != 0)) {
|
|
continue;
|
|
}
|
|
} break;
|
|
case ucode::ControlFlowOpcode::kCondExecPred:
|
|
case ucode::ControlFlowOpcode::kCondExecPredEnd: {
|
|
const ucode::ControlFlowCondExecPredInstruction cf_cond_exec_pred =
|
|
*reinterpret_cast<
|
|
const ucode::ControlFlowCondExecPredInstruction*>(&cf_exec);
|
|
if (cf_cond_exec_pred.condition() != state_.predicate) {
|
|
continue;
|
|
}
|
|
} break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
for (uint32_t exec_index = 0; exec_index < cf_exec.count();
|
|
++exec_index) {
|
|
const uint32_t* exec_instruction =
|
|
&ucode_[3 * (cf_exec.address() + exec_index)];
|
|
if ((cf_exec.sequence() >> (exec_index << 1)) & 0b01) {
|
|
const ucode::FetchInstruction& fetch_instr =
|
|
*reinterpret_cast<const ucode::FetchInstruction*>(
|
|
exec_instruction);
|
|
if (fetch_instr.is_predicated() &&
|
|
fetch_instr.predicate_condition() != state_.predicate) {
|
|
continue;
|
|
}
|
|
if (fetch_instr.opcode() == ucode::FetchOpcode::kVertexFetch) {
|
|
ExecuteVertexFetchInstruction(fetch_instr.vertex_fetch());
|
|
} else {
|
|
// Not supporting texture fetching (very complex).
|
|
float zero_result[4] = {};
|
|
StoreFetchResult(fetch_instr.dest(),
|
|
fetch_instr.is_dest_relative(),
|
|
fetch_instr.dest_swizzle(), zero_result);
|
|
}
|
|
} else {
|
|
const ucode::AluInstruction& alu_instr =
|
|
*reinterpret_cast<const ucode::AluInstruction*>(
|
|
exec_instruction);
|
|
if (alu_instr.is_predicated() &&
|
|
alu_instr.predicate_condition() != state_.predicate) {
|
|
continue;
|
|
}
|
|
ExecuteAluInstruction(alu_instr);
|
|
}
|
|
}
|
|
|
|
if (ucode::DoesControlFlowOpcodeEndShader(cf_opcode)) {
|
|
exec_ended = true;
|
|
}
|
|
} break;
|
|
|
|
case ucode::ControlFlowOpcode::kLoopStart: {
|
|
ucode::ControlFlowLoopStartInstruction cf_loop_start =
|
|
*reinterpret_cast<const ucode::ControlFlowLoopStartInstruction*>(
|
|
&cf_instr);
|
|
assert_true(state_.loop_stack_depth < 4);
|
|
if (++state_.loop_stack_depth > 4) {
|
|
cf_index_next = cf_loop_start.address();
|
|
continue;
|
|
}
|
|
xenos::LoopConstant loop_constant =
|
|
loop_constants[cf_loop_start.loop_id()];
|
|
state_.loop_constants[state_.loop_stack_depth] = loop_constant;
|
|
uint32_t& loop_iterator_ref =
|
|
state_.loop_iterators[state_.loop_stack_depth];
|
|
if (!cf_loop_start.is_repeat()) {
|
|
loop_iterator_ref = 0;
|
|
}
|
|
if (loop_iterator_ref >= loop_constant.count) {
|
|
cf_index_next = cf_loop_start.address();
|
|
continue;
|
|
}
|
|
++state_.loop_stack_depth;
|
|
} break;
|
|
|
|
case ucode::ControlFlowOpcode::kLoopEnd: {
|
|
assert_not_zero(state_.loop_stack_depth);
|
|
if (!state_.loop_stack_depth) {
|
|
continue;
|
|
}
|
|
assert_true(state_.loop_stack_depth <= 4);
|
|
if (state_.loop_stack_depth > 4) {
|
|
--state_.loop_stack_depth;
|
|
continue;
|
|
}
|
|
ucode::ControlFlowLoopEndInstruction cf_loop_end =
|
|
*reinterpret_cast<const ucode::ControlFlowLoopEndInstruction*>(
|
|
&cf_instr);
|
|
xenos::LoopConstant loop_constant =
|
|
state_.loop_constants[state_.loop_stack_depth - 1];
|
|
assert_true(loop_constant.value ==
|
|
loop_constants[cf_loop_end.loop_id()].value);
|
|
uint32_t loop_iterator =
|
|
++state_.loop_iterators[state_.loop_stack_depth - 1];
|
|
if (loop_iterator < loop_constant.count &&
|
|
(!cf_loop_end.is_predicated_break() ||
|
|
cf_loop_end.condition() != state_.predicate)) {
|
|
cf_index_next = cf_loop_end.address();
|
|
continue;
|
|
}
|
|
--state_.loop_stack_depth;
|
|
} break;
|
|
|
|
case ucode::ControlFlowOpcode::kCondCall: {
|
|
assert_true(state_.call_stack_depth < 4);
|
|
if (state_.call_stack_depth >= 4) {
|
|
continue;
|
|
}
|
|
const ucode::ControlFlowCondCallInstruction cf_cond_call =
|
|
*reinterpret_cast<const ucode::ControlFlowCondCallInstruction*>(
|
|
&cf_instr);
|
|
if (!cf_cond_call.is_unconditional()) {
|
|
if (cf_cond_call.is_predicated()) {
|
|
if (cf_cond_call.condition() != state_.predicate) {
|
|
continue;
|
|
}
|
|
} else {
|
|
uint32_t bool_address = cf_cond_call.bool_address();
|
|
if (cf_cond_call.condition() !=
|
|
((bool_constants[bool_address >> 5] &
|
|
(UINT32_C(1) << (bool_address & 31))) != 0)) {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
state_.call_return_addresses[state_.call_stack_depth++] = cf_index + 1;
|
|
cf_index_next = cf_cond_call.address();
|
|
} break;
|
|
|
|
case ucode::ControlFlowOpcode::kReturn: {
|
|
// No stack depth assertion - skipping the return is a well-defined
|
|
// behavior for `return` outside a function call.
|
|
if (!state_.call_stack_depth) {
|
|
continue;
|
|
}
|
|
cf_index_next = state_.call_return_addresses[--state_.call_stack_depth];
|
|
} break;
|
|
|
|
case ucode::ControlFlowOpcode::kCondJmp: {
|
|
const ucode::ControlFlowCondJmpInstruction cf_cond_jmp =
|
|
*reinterpret_cast<const ucode::ControlFlowCondJmpInstruction*>(
|
|
&cf_instr);
|
|
if (!cf_cond_jmp.is_unconditional()) {
|
|
if (cf_cond_jmp.is_predicated()) {
|
|
if (cf_cond_jmp.condition() != state_.predicate) {
|
|
continue;
|
|
}
|
|
} else {
|
|
uint32_t bool_address = cf_cond_jmp.bool_address();
|
|
if (cf_cond_jmp.condition() !=
|
|
((bool_constants[bool_address >> 5] &
|
|
(UINT32_C(1) << (bool_address & 31))) != 0)) {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
cf_index_next = cf_cond_jmp.address();
|
|
} break;
|
|
|
|
case ucode::ControlFlowOpcode::kAlloc: {
|
|
if (export_sink_) {
|
|
const ucode::ControlFlowAllocInstruction& cf_alloc =
|
|
*reinterpret_cast<const ucode::ControlFlowAllocInstruction*>(
|
|
&cf_instr);
|
|
export_sink_->AllocExport(cf_alloc.alloc_type(), cf_alloc.size());
|
|
}
|
|
} break;
|
|
|
|
case ucode::ControlFlowOpcode::kMarkVsFetchDone: {
|
|
} break;
|
|
|
|
default:
|
|
assert_unhandled_case(cf_opcode);
|
|
}
|
|
}
|
|
}
|
|
|
|
const float* ShaderInterpreter::GetFloatConstant(
|
|
uint32_t address, bool is_relative, bool relative_address_is_a0) const {
|
|
static const float zero[4] = {};
|
|
int32_t index = int32_t(address);
|
|
if (is_relative) {
|
|
index += relative_address_is_a0 ? state_.address_register
|
|
: state_.GetLoopAddress();
|
|
}
|
|
if (index < 0) {
|
|
return zero;
|
|
}
|
|
auto base_and_size_minus_1 = register_file_.Get<reg::SQ_VS_CONST>(
|
|
shader_type_ == xenos::ShaderType::kVertex ? XE_GPU_REG_SQ_VS_CONST
|
|
: XE_GPU_REG_SQ_PS_CONST);
|
|
if (uint32_t(index) > base_and_size_minus_1.size) {
|
|
return zero;
|
|
}
|
|
index += base_and_size_minus_1.base;
|
|
if (index >= 512) {
|
|
return zero;
|
|
}
|
|
return ®ister_file_[XE_GPU_REG_SHADER_CONSTANT_000_X + 4 * index].f32;
|
|
}
|
|
|
|
void ShaderInterpreter::ExecuteAluInstruction(ucode::AluInstruction instr) {
|
|
// Vector operation.
|
|
float vector_result[4] = {};
|
|
ucode::AluVectorOpcode vector_opcode = instr.vector_opcode();
|
|
const ucode::AluVectorOpcodeInfo& vector_opcode_info =
|
|
ucode::GetAluVectorOpcodeInfo(vector_opcode);
|
|
uint32_t vector_result_write_mask = instr.GetVectorOpResultWriteMask();
|
|
if (vector_result_write_mask || vector_opcode_info.changed_state) {
|
|
float vector_operands[3][4];
|
|
for (uint32_t i = 0; i < 3; ++i) {
|
|
if (!vector_opcode_info.operand_components_used[i]) {
|
|
continue;
|
|
}
|
|
const float* vector_src_ptr;
|
|
uint32_t vector_src_register = instr.src_reg(1 + i);
|
|
bool vector_src_absolute = false;
|
|
if (instr.src_is_temp(1 + i)) {
|
|
vector_src_ptr = GetTempRegister(
|
|
ucode::AluInstruction::src_temp_reg(vector_src_register),
|
|
ucode::AluInstruction::is_src_temp_relative(vector_src_register));
|
|
vector_src_absolute = ucode::AluInstruction::is_src_temp_value_absolute(
|
|
vector_src_register);
|
|
} else {
|
|
vector_src_ptr = GetFloatConstant(
|
|
vector_src_register, instr.src_const_is_addressed(1 + i),
|
|
instr.is_const_address_register_relative());
|
|
}
|
|
uint32_t vector_src_absolute_mask =
|
|
~(uint32_t(vector_src_absolute) << 31);
|
|
uint32_t vector_src_negate_bit = uint32_t(instr.src_negate(1 + i)) << 31;
|
|
uint32_t vector_src_swizzle = instr.src_swizzle(1 + i);
|
|
for (uint32_t j = 0; j < 4; ++j) {
|
|
float vector_src_component = FlushDenormal(
|
|
vector_src_ptr[ucode::AluInstruction::GetSwizzledComponentIndex(
|
|
vector_src_swizzle, j)]);
|
|
*reinterpret_cast<uint32_t*>(&vector_src_component) =
|
|
(*reinterpret_cast<const uint32_t*>(&vector_src_component) &
|
|
vector_src_absolute_mask) ^
|
|
vector_src_negate_bit;
|
|
vector_operands[i][j] = vector_src_component;
|
|
}
|
|
}
|
|
|
|
bool replicate_vector_result_x = false;
|
|
switch (vector_opcode) {
|
|
case ucode::AluVectorOpcode::kAdd: {
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
vector_result[i] = vector_operands[0][i] + vector_operands[1][i];
|
|
}
|
|
} break;
|
|
case ucode::AluVectorOpcode::kMul: {
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
// Direct3D 9 behavior (0 or denormal * anything = +0).
|
|
vector_result[i] = (vector_operands[0][i] && vector_operands[1][i])
|
|
? vector_operands[0][i] * vector_operands[1][i]
|
|
: 0.0f;
|
|
}
|
|
} break;
|
|
case ucode::AluVectorOpcode::kMax: {
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
vector_result[i] = vector_operands[0][i] >= vector_operands[1][i]
|
|
? vector_operands[0][i]
|
|
: vector_operands[1][i];
|
|
}
|
|
} break;
|
|
case ucode::AluVectorOpcode::kMin: {
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
vector_result[i] = vector_operands[0][i] < vector_operands[1][i]
|
|
? vector_operands[0][i]
|
|
: vector_operands[1][i];
|
|
}
|
|
} break;
|
|
case ucode::AluVectorOpcode::kSeq: {
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
vector_result[i] =
|
|
float(vector_operands[0][i] == vector_operands[1][i]);
|
|
}
|
|
} break;
|
|
case ucode::AluVectorOpcode::kSgt: {
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
vector_result[i] =
|
|
float(vector_operands[0][i] > vector_operands[1][i]);
|
|
}
|
|
} break;
|
|
case ucode::AluVectorOpcode::kSge: {
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
vector_result[i] =
|
|
float(vector_operands[0][i] >= vector_operands[1][i]);
|
|
}
|
|
} break;
|
|
case ucode::AluVectorOpcode::kSne: {
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
vector_result[i] =
|
|
float(vector_operands[0][i] != vector_operands[1][i]);
|
|
}
|
|
} break;
|
|
case ucode::AluVectorOpcode::kFrc: {
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
vector_result[i] =
|
|
vector_operands[0][i] - std::floor(vector_operands[0][i]);
|
|
}
|
|
} break;
|
|
case ucode::AluVectorOpcode::kTrunc: {
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
vector_result[i] = std::trunc(vector_operands[0][i]);
|
|
}
|
|
} break;
|
|
case ucode::AluVectorOpcode::kFloor: {
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
vector_result[i] = std::floor(vector_operands[0][i]);
|
|
}
|
|
} break;
|
|
case ucode::AluVectorOpcode::kMad: {
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
// Direct3D 9 behavior (0 or denormal * anything = +0).
|
|
// Doing the addition rather than conditional assignment even for zero
|
|
// operands because +0 + -0 must be +0.
|
|
vector_result[i] =
|
|
((vector_operands[0][i] && vector_operands[1][i])
|
|
? vector_operands[0][i] * vector_operands[1][i]
|
|
: 0.0f) +
|
|
vector_operands[2][i];
|
|
}
|
|
} break;
|
|
case ucode::AluVectorOpcode::kCndEq: {
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
vector_result[i] = vector_operands[0][i] == 0.0f
|
|
? vector_operands[1][i]
|
|
: vector_operands[2][i];
|
|
}
|
|
} break;
|
|
case ucode::AluVectorOpcode::kCndGe: {
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
vector_result[i] = vector_operands[0][i] >= 0.0f
|
|
? vector_operands[1][i]
|
|
: vector_operands[2][i];
|
|
}
|
|
} break;
|
|
case ucode::AluVectorOpcode::kCndGt: {
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
vector_result[i] = vector_operands[0][i] > 0.0f
|
|
? vector_operands[1][i]
|
|
: vector_operands[2][i];
|
|
}
|
|
} break;
|
|
case ucode::AluVectorOpcode::kDp4: {
|
|
vector_result[0] = 0.0f;
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
// Direct3D 9 behavior (0 or denormal * anything = +0).
|
|
// Doing the addition even for zero operands because +0 + -0 must be
|
|
// +0.
|
|
vector_result[0] +=
|
|
(vector_operands[0][i] && vector_operands[1][i])
|
|
? vector_operands[0][i] * vector_operands[1][i]
|
|
: 0.0f;
|
|
}
|
|
replicate_vector_result_x = true;
|
|
} break;
|
|
case ucode::AluVectorOpcode::kDp3: {
|
|
vector_result[0] = 0.0f;
|
|
for (uint32_t i = 0; i < 3; ++i) {
|
|
// Direct3D 9 behavior (0 or denormal * anything = +0).
|
|
// Doing the addition even for zero operands because +0 + -0 must be
|
|
// +0.
|
|
vector_result[0] +=
|
|
(vector_operands[0][i] && vector_operands[1][i])
|
|
? vector_operands[0][i] * vector_operands[1][i]
|
|
: 0.0f;
|
|
}
|
|
replicate_vector_result_x = true;
|
|
} break;
|
|
case ucode::AluVectorOpcode::kDp2Add: {
|
|
// Doing the addition even for zero operands because +0 + -0 must be +0.
|
|
vector_result[0] = 0.0f;
|
|
for (uint32_t i = 0; i < 2; ++i) {
|
|
// Direct3D 9 behavior (0 or denormal * anything = +0).
|
|
vector_result[0] +=
|
|
(vector_operands[0][i] && vector_operands[1][i])
|
|
? vector_operands[0][i] * vector_operands[1][i]
|
|
: 0.0f;
|
|
}
|
|
vector_result[0] += vector_operands[2][0];
|
|
replicate_vector_result_x = true;
|
|
} break;
|
|
case ucode::AluVectorOpcode::kCube: {
|
|
// Operand [0] is .z_xy.
|
|
float x = vector_operands[0][2];
|
|
float y = vector_operands[0][3];
|
|
float z = vector_operands[0][0];
|
|
float x_abs = std::abs(x), y_abs = std::abs(y), z_abs = std::abs(z);
|
|
// Result is T coordinate, S coordinate, 2 * major axis, face ID.
|
|
if (z_abs >= x_abs && z_abs >= y_abs) {
|
|
vector_result[0] = -y;
|
|
vector_result[1] = z < 0.0f ? -x : x;
|
|
vector_result[2] = z;
|
|
vector_result[3] = z < 0.0f ? 5.0f : 4.0f;
|
|
} else if (y_abs >= x_abs) {
|
|
vector_result[0] = y < 0.0f ? -z : z;
|
|
vector_result[1] = x;
|
|
vector_result[2] = y;
|
|
vector_result[3] = y < 0.0f ? 3.0f : 2.0f;
|
|
} else {
|
|
vector_result[0] = -y;
|
|
vector_result[1] = x < 0.0f ? z : -z;
|
|
vector_result[2] = x;
|
|
vector_result[3] = x < 0.0f ? 1.0f : 0.0f;
|
|
}
|
|
vector_result[2] *= 2.0f;
|
|
} break;
|
|
case ucode::AluVectorOpcode::kMax4: {
|
|
if (vector_operands[0][0] >= vector_operands[0][1] &&
|
|
vector_operands[0][0] >= vector_operands[0][2] &&
|
|
vector_operands[0][0] >= vector_operands[0][3]) {
|
|
vector_result[0] = vector_operands[0][0];
|
|
} else if (vector_operands[0][1] >= vector_operands[0][2] &&
|
|
vector_operands[0][1] >= vector_operands[0][3]) {
|
|
vector_result[0] = vector_operands[0][1];
|
|
} else if (vector_operands[0][2] >= vector_operands[0][3]) {
|
|
vector_result[0] = vector_operands[0][2];
|
|
} else {
|
|
vector_result[0] = vector_operands[0][3];
|
|
}
|
|
replicate_vector_result_x = true;
|
|
} break;
|
|
case ucode::AluVectorOpcode::kSetpEqPush: {
|
|
state_.predicate =
|
|
vector_operands[0][3] == 0.0f && vector_operands[1][3] == 0.0f;
|
|
vector_result[0] =
|
|
(vector_operands[0][0] == 0.0f && vector_operands[1][0] == 0.0f)
|
|
? 0.0f
|
|
: vector_operands[0][0] + 1.0f;
|
|
replicate_vector_result_x = true;
|
|
} break;
|
|
case ucode::AluVectorOpcode::kSetpNePush: {
|
|
state_.predicate =
|
|
vector_operands[0][3] == 0.0f && vector_operands[1][3] != 0.0f;
|
|
vector_result[0] =
|
|
(vector_operands[0][0] == 0.0f && vector_operands[1][0] != 0.0f)
|
|
? 0.0f
|
|
: vector_operands[0][0] + 1.0f;
|
|
replicate_vector_result_x = true;
|
|
} break;
|
|
case ucode::AluVectorOpcode::kSetpGtPush: {
|
|
state_.predicate =
|
|
vector_operands[0][3] == 0.0f && vector_operands[1][3] > 0.0f;
|
|
vector_result[0] =
|
|
(vector_operands[0][0] == 0.0f && vector_operands[1][0] > 0.0f)
|
|
? 0.0f
|
|
: vector_operands[0][0] + 1.0f;
|
|
replicate_vector_result_x = true;
|
|
} break;
|
|
case ucode::AluVectorOpcode::kSetpGePush: {
|
|
state_.predicate =
|
|
vector_operands[0][3] == 0.0f && vector_operands[1][3] >= 0.0f;
|
|
vector_result[0] =
|
|
(vector_operands[0][0] == 0.0f && vector_operands[1][0] >= 0.0f)
|
|
? 0.0f
|
|
: vector_operands[0][0] + 1.0f;
|
|
replicate_vector_result_x = true;
|
|
} break;
|
|
// Not implementing pixel kill currently, the interpreter is currently
|
|
// used only for vertex shaders.
|
|
case ucode::AluVectorOpcode::kKillEq: {
|
|
vector_result[0] =
|
|
float(vector_operands[0][0] == vector_operands[1][0] ||
|
|
vector_operands[0][1] == vector_operands[1][1] ||
|
|
vector_operands[0][2] == vector_operands[1][2] ||
|
|
vector_operands[0][3] == vector_operands[1][3]);
|
|
replicate_vector_result_x = true;
|
|
} break;
|
|
case ucode::AluVectorOpcode::kKillGt: {
|
|
vector_result[0] =
|
|
float(vector_operands[0][0] > vector_operands[1][0] ||
|
|
vector_operands[0][1] > vector_operands[1][1] ||
|
|
vector_operands[0][2] > vector_operands[1][2] ||
|
|
vector_operands[0][3] > vector_operands[1][3]);
|
|
replicate_vector_result_x = true;
|
|
} break;
|
|
case ucode::AluVectorOpcode::kKillGe: {
|
|
vector_result[0] =
|
|
float(vector_operands[0][0] >= vector_operands[1][0] ||
|
|
vector_operands[0][1] >= vector_operands[1][1] ||
|
|
vector_operands[0][2] >= vector_operands[1][2] ||
|
|
vector_operands[0][3] >= vector_operands[1][3]);
|
|
replicate_vector_result_x = true;
|
|
} break;
|
|
case ucode::AluVectorOpcode::kKillNe: {
|
|
vector_result[0] =
|
|
float(vector_operands[0][0] != vector_operands[1][0] ||
|
|
vector_operands[0][1] != vector_operands[1][1] ||
|
|
vector_operands[0][2] != vector_operands[1][2] ||
|
|
vector_operands[0][3] != vector_operands[1][3]);
|
|
replicate_vector_result_x = true;
|
|
} break;
|
|
case ucode::AluVectorOpcode::kDst: {
|
|
vector_result[0] = 1.0f;
|
|
// Direct3D 9 behavior (0 or denormal * anything = +0).
|
|
vector_result[1] = (vector_operands[0][1] && vector_operands[1][1])
|
|
? vector_operands[0][1] * vector_operands[1][1]
|
|
: 0.0f;
|
|
vector_result[2] = vector_operands[0][2];
|
|
vector_result[3] = vector_operands[1][3];
|
|
} break;
|
|
case ucode::AluVectorOpcode::kMaxA: {
|
|
// std::max is `a < b ? b : a`, thus in case of NaN, the first argument
|
|
// (-256.0f) is always the result.
|
|
state_.address_register = int32_t(std::floor(
|
|
std::min(255.0f, std::max(-256.0f, vector_operands[0][3])) + 0.5f));
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
vector_result[i] = vector_operands[0][i] >= vector_operands[1][i]
|
|
? vector_operands[0][i]
|
|
: vector_operands[1][i];
|
|
}
|
|
} break;
|
|
default: {
|
|
assert_unhandled_case(vector_opcode);
|
|
}
|
|
}
|
|
if (replicate_vector_result_x) {
|
|
for (uint32_t i = 1; i < 4; ++i) {
|
|
vector_result[i] = vector_result[0];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Scalar operation.
|
|
ucode::AluScalarOpcode scalar_opcode = instr.scalar_opcode();
|
|
const ucode::AluScalarOpcodeInfo& scalar_opcode_info =
|
|
ucode::GetAluScalarOpcodeInfo(scalar_opcode);
|
|
float scalar_operands[2];
|
|
uint32_t scalar_operand_component_count = 0;
|
|
bool scalar_src_absolute = false;
|
|
switch (scalar_opcode_info.operand_count) {
|
|
case 1: {
|
|
// r#/c#.w or r#/c#.wx.
|
|
const float* scalar_src_ptr;
|
|
uint32_t scalar_src_register = instr.src_reg(3);
|
|
if (instr.src_is_temp(3)) {
|
|
scalar_src_ptr = GetTempRegister(
|
|
ucode::AluInstruction::src_temp_reg(scalar_src_register),
|
|
ucode::AluInstruction::is_src_temp_relative(scalar_src_register));
|
|
scalar_src_absolute = ucode::AluInstruction::is_src_temp_value_absolute(
|
|
scalar_src_register);
|
|
} else {
|
|
scalar_src_ptr = GetFloatConstant(
|
|
scalar_src_register, instr.src_const_is_addressed(3),
|
|
instr.is_const_address_register_relative());
|
|
}
|
|
uint32_t scalar_src_swizzle = instr.src_swizzle(3);
|
|
scalar_operand_component_count =
|
|
scalar_opcode_info.single_operand_is_two_component ? 2 : 1;
|
|
for (uint32_t i = 0; i < scalar_operand_component_count; ++i) {
|
|
scalar_operands[i] =
|
|
scalar_src_ptr[ucode::AluInstruction::GetSwizzledComponentIndex(
|
|
scalar_src_swizzle, (3 + i) & 3)];
|
|
}
|
|
} break;
|
|
case 2: {
|
|
scalar_operand_component_count = 2;
|
|
uint32_t scalar_src_absolute_mask =
|
|
~(uint32_t(instr.abs_constants()) << 31);
|
|
uint32_t scalar_src_negate_bit = uint32_t(instr.src_negate(3)) << 31;
|
|
uint32_t scalar_src_swizzle = instr.src_swizzle(3);
|
|
// c#.w.
|
|
scalar_operands[0] =
|
|
GetFloatConstant(instr.src_reg(3), instr.src_const_is_addressed(3),
|
|
instr.is_const_address_register_relative())
|
|
[ucode::AluInstruction::GetSwizzledComponentIndex(
|
|
scalar_src_swizzle, 3)];
|
|
// r#.x.
|
|
scalar_operands[1] = GetTempRegister(
|
|
instr.scalar_const_reg_op_src_temp_reg(),
|
|
false)[ucode::AluInstruction::GetSwizzledComponentIndex(
|
|
scalar_src_swizzle, 0)];
|
|
} break;
|
|
}
|
|
if (scalar_operand_component_count) {
|
|
uint32_t scalar_src_absolute_mask = ~(uint32_t(scalar_src_absolute) << 31);
|
|
uint32_t scalar_src_negate_bit = uint32_t(instr.src_negate(3)) << 31;
|
|
for (uint32_t i = 0; i < scalar_operand_component_count; ++i) {
|
|
float scalar_operand = FlushDenormal(scalar_operands[i]);
|
|
*reinterpret_cast<uint32_t*>(&scalar_operand) =
|
|
(*reinterpret_cast<const uint32_t*>(&scalar_operand) &
|
|
scalar_src_absolute_mask) ^
|
|
scalar_src_negate_bit;
|
|
scalar_operands[i] = scalar_operand;
|
|
}
|
|
}
|
|
switch (scalar_opcode) {
|
|
case ucode::AluScalarOpcode::kAdds:
|
|
case ucode::AluScalarOpcode::kAddsc0:
|
|
case ucode::AluScalarOpcode::kAddsc1: {
|
|
state_.previous_scalar = scalar_operands[0] + scalar_operands[1];
|
|
} break;
|
|
case ucode::AluScalarOpcode::kAddsPrev: {
|
|
state_.previous_scalar = scalar_operands[0] + state_.previous_scalar;
|
|
} break;
|
|
case ucode::AluScalarOpcode::kMuls:
|
|
case ucode::AluScalarOpcode::kMulsc0:
|
|
case ucode::AluScalarOpcode::kMulsc1: {
|
|
// Direct3D 9 behavior (0 or denormal * anything = +0).
|
|
state_.previous_scalar = (scalar_operands[0] && scalar_operands[1])
|
|
? scalar_operands[0] * scalar_operands[1]
|
|
: 0.0f;
|
|
} break;
|
|
case ucode::AluScalarOpcode::kMulsPrev: {
|
|
// Direct3D 9 behavior (0 or denormal * anything = +0).
|
|
state_.previous_scalar = (scalar_operands[0] && state_.previous_scalar)
|
|
? scalar_operands[0] * state_.previous_scalar
|
|
: 0.0f;
|
|
} break;
|
|
case ucode::AluScalarOpcode::kMulsPrev2: {
|
|
if (state_.previous_scalar == -FLT_MAX ||
|
|
!std::isfinite(state_.previous_scalar) ||
|
|
!std::isfinite(scalar_operands[1]) || scalar_operands[1] <= 0.0f) {
|
|
state_.previous_scalar = -FLT_MAX;
|
|
} else {
|
|
// Direct3D 9 behavior (0 or denormal * anything = +0).
|
|
state_.previous_scalar =
|
|
(scalar_operands[0] && state_.previous_scalar)
|
|
? scalar_operands[0] * state_.previous_scalar
|
|
: 0.0f;
|
|
}
|
|
} break;
|
|
case ucode::AluScalarOpcode::kMaxs: {
|
|
state_.previous_scalar = scalar_operands[0] >= scalar_operands[1]
|
|
? scalar_operands[0]
|
|
: scalar_operands[1];
|
|
} break;
|
|
case ucode::AluScalarOpcode::kMins: {
|
|
state_.previous_scalar = scalar_operands[0] >= scalar_operands[1]
|
|
? scalar_operands[0]
|
|
: scalar_operands[1];
|
|
} break;
|
|
case ucode::AluScalarOpcode::kSeqs: {
|
|
state_.previous_scalar = float(scalar_operands[0] == 0.0f);
|
|
} break;
|
|
case ucode::AluScalarOpcode::kSgts: {
|
|
state_.previous_scalar = float(scalar_operands[0] > 0.0f);
|
|
} break;
|
|
case ucode::AluScalarOpcode::kSges: {
|
|
state_.previous_scalar = float(scalar_operands[0] >= 0.0f);
|
|
} break;
|
|
case ucode::AluScalarOpcode::kSnes: {
|
|
state_.previous_scalar = float(scalar_operands[0] != 0.0f);
|
|
} break;
|
|
case ucode::AluScalarOpcode::kFrcs: {
|
|
state_.previous_scalar =
|
|
scalar_operands[0] - std::floor(scalar_operands[0]);
|
|
} break;
|
|
case ucode::AluScalarOpcode::kTruncs: {
|
|
state_.previous_scalar = std::trunc(scalar_operands[0]);
|
|
} break;
|
|
case ucode::AluScalarOpcode::kFloors: {
|
|
state_.previous_scalar = std::floor(scalar_operands[0]);
|
|
} break;
|
|
case ucode::AluScalarOpcode::kExp: {
|
|
state_.previous_scalar = std::exp2(scalar_operands[0]);
|
|
} break;
|
|
case ucode::AluScalarOpcode::kLogc: {
|
|
state_.previous_scalar = std::log2(scalar_operands[0]);
|
|
if (state_.previous_scalar == -INFINITY) {
|
|
state_.previous_scalar = -FLT_MAX;
|
|
}
|
|
} break;
|
|
case ucode::AluScalarOpcode::kLog: {
|
|
state_.previous_scalar = std::log2(scalar_operands[0]);
|
|
} break;
|
|
case ucode::AluScalarOpcode::kRcpc: {
|
|
state_.previous_scalar = 1.0f / scalar_operands[0];
|
|
if (state_.previous_scalar == -INFINITY) {
|
|
state_.previous_scalar = -FLT_MAX;
|
|
} else if (state_.previous_scalar == INFINITY) {
|
|
state_.previous_scalar = FLT_MAX;
|
|
}
|
|
} break;
|
|
case ucode::AluScalarOpcode::kRcpf: {
|
|
state_.previous_scalar = 1.0f / scalar_operands[0];
|
|
if (state_.previous_scalar == -INFINITY) {
|
|
state_.previous_scalar = -0.0f;
|
|
} else if (state_.previous_scalar == INFINITY) {
|
|
state_.previous_scalar = 0.0f;
|
|
}
|
|
} break;
|
|
case ucode::AluScalarOpcode::kRcp: {
|
|
state_.previous_scalar = 1.0f / scalar_operands[0];
|
|
} break;
|
|
case ucode::AluScalarOpcode::kRsqc: {
|
|
state_.previous_scalar = 1.0f / std::sqrt(scalar_operands[0]);
|
|
if (state_.previous_scalar == -INFINITY) {
|
|
state_.previous_scalar = -FLT_MAX;
|
|
} else if (state_.previous_scalar == INFINITY) {
|
|
state_.previous_scalar = FLT_MAX;
|
|
}
|
|
} break;
|
|
case ucode::AluScalarOpcode::kRsqf: {
|
|
state_.previous_scalar = 1.0f / std::sqrt(scalar_operands[0]);
|
|
if (state_.previous_scalar == -INFINITY) {
|
|
state_.previous_scalar = -0.0f;
|
|
} else if (state_.previous_scalar == INFINITY) {
|
|
state_.previous_scalar = 0.0f;
|
|
}
|
|
} break;
|
|
case ucode::AluScalarOpcode::kRsq: {
|
|
state_.previous_scalar = 1.0f / std::sqrt(scalar_operands[0]);
|
|
} break;
|
|
case ucode::AluScalarOpcode::kMaxAs: {
|
|
// std::max is `a < b ? b : a`, thus in case of NaN, the first argument
|
|
// (-256.0f) is always the result.
|
|
state_.address_register = int32_t(std::floor(
|
|
std::min(255.0f, std::max(-256.0f, scalar_operands[0])) + 0.5f));
|
|
state_.previous_scalar = scalar_operands[0] >= scalar_operands[1]
|
|
? scalar_operands[0]
|
|
: scalar_operands[1];
|
|
} break;
|
|
case ucode::AluScalarOpcode::kMaxAsf: {
|
|
// std::max is `a < b ? b : a`, thus in case of NaN, the first argument
|
|
// (-256.0f) is always the result.
|
|
state_.address_register = int32_t(
|
|
std::floor(std::min(255.0f, std::max(-256.0f, scalar_operands[0]))));
|
|
state_.previous_scalar = scalar_operands[0] >= scalar_operands[1]
|
|
? scalar_operands[0]
|
|
: scalar_operands[1];
|
|
} break;
|
|
case ucode::AluScalarOpcode::kSubs:
|
|
case ucode::AluScalarOpcode::kSubsc0:
|
|
case ucode::AluScalarOpcode::kSubsc1: {
|
|
state_.previous_scalar = scalar_operands[0] - scalar_operands[1];
|
|
} break;
|
|
case ucode::AluScalarOpcode::kSubsPrev: {
|
|
state_.previous_scalar = scalar_operands[0] - state_.previous_scalar;
|
|
} break;
|
|
case ucode::AluScalarOpcode::kSetpEq: {
|
|
state_.predicate = scalar_operands[0] == 0.0f;
|
|
state_.previous_scalar = float(!state_.predicate);
|
|
} break;
|
|
case ucode::AluScalarOpcode::kSetpNe: {
|
|
state_.predicate = scalar_operands[0] != 0.0f;
|
|
state_.previous_scalar = float(!state_.predicate);
|
|
} break;
|
|
case ucode::AluScalarOpcode::kSetpGt: {
|
|
state_.predicate = scalar_operands[0] > 0.0f;
|
|
state_.previous_scalar = float(!state_.predicate);
|
|
} break;
|
|
case ucode::AluScalarOpcode::kSetpGe: {
|
|
state_.predicate = scalar_operands[0] >= 0.0f;
|
|
state_.previous_scalar = float(!state_.predicate);
|
|
} break;
|
|
case ucode::AluScalarOpcode::kSetpInv: {
|
|
state_.predicate = scalar_operands[0] == 1.0f;
|
|
state_.previous_scalar =
|
|
state_.predicate
|
|
? 0.0f
|
|
: (scalar_operands[0] == 0.0f ? 1.0f : scalar_operands[0]);
|
|
} break;
|
|
case ucode::AluScalarOpcode::kSetpPop: {
|
|
float new_counter = scalar_operands[0] - 1.0f;
|
|
state_.predicate = new_counter <= 0.0f;
|
|
state_.previous_scalar = state_.predicate ? 0.0f : new_counter;
|
|
} break;
|
|
case ucode::AluScalarOpcode::kSetpClr: {
|
|
state_.predicate = false;
|
|
state_.previous_scalar = FLT_MAX;
|
|
} break;
|
|
case ucode::AluScalarOpcode::kSetpRstr: {
|
|
state_.predicate = scalar_operands[0] == 0.0f;
|
|
state_.previous_scalar = state_.predicate ? 0.0f : scalar_operands[0];
|
|
} break;
|
|
// Not implementing pixel kill currently, the interpreter is currently used
|
|
// only for vertex shaders.
|
|
case ucode::AluScalarOpcode::kKillsEq: {
|
|
state_.previous_scalar = float(scalar_operands[0] == 0.0f);
|
|
} break;
|
|
case ucode::AluScalarOpcode::kKillsGt: {
|
|
state_.previous_scalar = float(scalar_operands[0] > 0.0f);
|
|
} break;
|
|
case ucode::AluScalarOpcode::kKillsGe: {
|
|
state_.previous_scalar = float(scalar_operands[0] >= 0.0f);
|
|
} break;
|
|
case ucode::AluScalarOpcode::kKillsNe: {
|
|
state_.previous_scalar = float(scalar_operands[0] != 0.0f);
|
|
} break;
|
|
case ucode::AluScalarOpcode::kKillsOne: {
|
|
state_.previous_scalar = float(scalar_operands[0] == 1.0f);
|
|
} break;
|
|
case ucode::AluScalarOpcode::kSqrt: {
|
|
state_.previous_scalar = std::sqrt(scalar_operands[0]);
|
|
} break;
|
|
case ucode::AluScalarOpcode::kSin: {
|
|
state_.previous_scalar = std::sin(scalar_operands[0]);
|
|
} break;
|
|
case ucode::AluScalarOpcode::kCos: {
|
|
state_.previous_scalar = std::cos(scalar_operands[0]);
|
|
} break;
|
|
case ucode::AluScalarOpcode::kRetainPrev: {
|
|
} break;
|
|
default: {
|
|
assert_unhandled_case(scalar_opcode);
|
|
}
|
|
}
|
|
|
|
if (instr.vector_clamp()) {
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
vector_result[i] = xe::saturate_unsigned(vector_result[i]);
|
|
}
|
|
}
|
|
float scalar_result = instr.scalar_clamp()
|
|
? xe::saturate_unsigned(state_.previous_scalar)
|
|
: state_.previous_scalar;
|
|
|
|
uint32_t scalar_result_write_mask = instr.GetScalarOpResultWriteMask();
|
|
if (instr.is_export()) {
|
|
if (export_sink_) {
|
|
float export_value[4];
|
|
uint32_t export_constant_1_mask = instr.GetConstant1WriteMask();
|
|
uint32_t export_mask =
|
|
vector_result_write_mask | scalar_result_write_mask |
|
|
instr.GetConstant0WriteMask() | export_constant_1_mask;
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
uint32_t export_component_bit = UINT32_C(1) << i;
|
|
float export_component = 0.0f;
|
|
if (vector_result_write_mask & export_component_bit) {
|
|
export_component = vector_result[i];
|
|
} else if (scalar_result_write_mask & export_component_bit) {
|
|
export_component = scalar_result;
|
|
} else if (export_constant_1_mask & export_component_bit) {
|
|
export_component = 1.0f;
|
|
} else {
|
|
export_component = 0.0f;
|
|
}
|
|
export_value[i] = export_component;
|
|
}
|
|
export_sink_->Export(
|
|
ucode::ExportRegister(instr.vector_dest()), export_value,
|
|
vector_result_write_mask | scalar_result_write_mask |
|
|
instr.GetConstant0WriteMask() | export_constant_1_mask);
|
|
}
|
|
} else {
|
|
if (vector_result_write_mask) {
|
|
float* vector_dest =
|
|
GetTempRegister(instr.vector_dest(), instr.is_vector_dest_relative());
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
if (vector_result_write_mask & (UINT32_C(1) << i)) {
|
|
vector_dest[i] = vector_result[i];
|
|
}
|
|
}
|
|
}
|
|
if (scalar_result_write_mask) {
|
|
float* scalar_dest =
|
|
GetTempRegister(instr.scalar_dest(), instr.is_scalar_dest_relative());
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
if (scalar_result_write_mask & (UINT32_C(1) << i)) {
|
|
scalar_dest[i] = scalar_result;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ShaderInterpreter::StoreFetchResult(uint32_t dest, bool is_dest_relative,
|
|
uint32_t swizzle, const float* value) {
|
|
float* dest_data = GetTempRegister(dest, is_dest_relative);
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
ucode::FetchDestinationSwizzle component_swizzle =
|
|
ucode::GetFetchDestinationComponentSwizzle(swizzle, i);
|
|
switch (component_swizzle) {
|
|
case ucode::FetchDestinationSwizzle::kX:
|
|
dest_data[i] = value[0];
|
|
break;
|
|
case ucode::FetchDestinationSwizzle::kY:
|
|
dest_data[i] = value[1];
|
|
break;
|
|
case ucode::FetchDestinationSwizzle::kZ:
|
|
dest_data[i] = value[2];
|
|
break;
|
|
case ucode::FetchDestinationSwizzle::kW:
|
|
dest_data[i] = value[3];
|
|
break;
|
|
case ucode::FetchDestinationSwizzle::k1:
|
|
dest_data[i] = 1.0f;
|
|
break;
|
|
case ucode::FetchDestinationSwizzle::kKeep:
|
|
break;
|
|
default:
|
|
// ucode::FetchDestinationSwizzle::k0 or the invalid swizzle 6.
|
|
// TODO(Triang3l): Find the correct handling of the invalid swizzle 6.
|
|
assert_true(component_swizzle == ucode::FetchDestinationSwizzle::k0);
|
|
dest_data[i] = 0.0f;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ShaderInterpreter::ExecuteVertexFetchInstruction(
|
|
ucode::VertexFetchInstruction instr) {
|
|
// FIXME(Triang3l): Bit scan loops over components cause a link-time
|
|
// optimization internal error in Visual Studio 2019, mainly in the format
|
|
// unpacking. Using loops with up to 4 iterations here instead.
|
|
|
|
if (!instr.is_mini_fetch()) {
|
|
state_.vfetch_full_last = instr;
|
|
}
|
|
|
|
xenos::xe_gpu_vertex_fetch_t fetch_constant =
|
|
*reinterpret_cast<const xenos::xe_gpu_vertex_fetch_t*>(
|
|
®ister_file_[XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0 +
|
|
state_.vfetch_full_last.fetch_constant_index()]);
|
|
|
|
if (!instr.is_mini_fetch()) {
|
|
// Get the part of the address that depends on vfetch_full data.
|
|
uint32_t vertex_index = uint32_t(std::floor(
|
|
GetTempRegister(instr.src(),
|
|
instr.is_src_relative())[instr.src_swizzle()] +
|
|
(instr.is_index_rounded() ? 0.5f : 0.0f)));
|
|
state_.vfetch_address_dwords =
|
|
instr.stride() * vertex_index + fetch_constant.address;
|
|
}
|
|
|
|
// TODO(Triang3l): Find the default values for unused components.
|
|
float result[4] = {};
|
|
uint32_t dest_swizzle = instr.dest_swizzle();
|
|
uint32_t used_result_components = 0b0000;
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
uint32_t dest_component_swizzle = (dest_swizzle >> (3 * i)) & 0b111;
|
|
if (dest_component_swizzle <= 3) {
|
|
used_result_components |= UINT32_C(1) << dest_component_swizzle;
|
|
}
|
|
}
|
|
uint32_t needed_dwords = xenos::GetVertexFormatNeededWords(
|
|
instr.data_format(), used_result_components);
|
|
if (needed_dwords) {
|
|
uint32_t data[4] = {};
|
|
const uint32_t* memory_dwords =
|
|
reinterpret_cast<const uint32_t*>(memory_.physical_membase());
|
|
uint32_t buffer_end_dwords = fetch_constant.address + fetch_constant.size;
|
|
uint32_t dword_0_address_dwords =
|
|
uint32_t(int32_t(state_.vfetch_address_dwords) + instr.offset());
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
if (!(needed_dwords & (UINT32_C(1) << i))) {
|
|
continue;
|
|
}
|
|
uint32_t dword_value = 0;
|
|
uint32_t dword_address_dwords = dword_0_address_dwords + i;
|
|
if (dword_address_dwords >= fetch_constant.address &&
|
|
dword_address_dwords < buffer_end_dwords) {
|
|
if (trace_writer_) {
|
|
trace_writer_->WriteMemoryRead(
|
|
sizeof(uint32_t) * dword_address_dwords, sizeof(uint32_t));
|
|
}
|
|
dword_value = xenos::GpuSwap(memory_dwords[dword_address_dwords],
|
|
fetch_constant.endian);
|
|
}
|
|
data[i] = dword_value;
|
|
}
|
|
|
|
uint32_t packed_components = 0b0000;
|
|
uint32_t packed_widths[4], packed_offsets[4];
|
|
uint32_t packed_dwords[] = {data[0], data[0]};
|
|
switch (instr.data_format()) {
|
|
case xenos::VertexFormat::k_8_8_8_8: {
|
|
packed_components = 0b1111;
|
|
packed_widths[0] = packed_widths[1] = packed_widths[2] =
|
|
packed_widths[3] = 8;
|
|
packed_offsets[1] = 8;
|
|
packed_offsets[2] = 16;
|
|
packed_offsets[3] = 24;
|
|
} break;
|
|
case xenos::VertexFormat::k_2_10_10_10: {
|
|
packed_components = 0b1111;
|
|
packed_widths[0] = packed_widths[1] = packed_widths[2] = 10;
|
|
packed_widths[3] = 2;
|
|
packed_offsets[1] = 10;
|
|
packed_offsets[2] = 20;
|
|
packed_offsets[3] = 30;
|
|
} break;
|
|
case xenos::VertexFormat::k_10_11_11: {
|
|
packed_components = 0b0111;
|
|
packed_widths[0] = packed_widths[1] = 11;
|
|
packed_widths[2] = 10;
|
|
packed_offsets[1] = 11;
|
|
packed_offsets[2] = 22;
|
|
} break;
|
|
case xenos::VertexFormat::k_11_11_10: {
|
|
packed_components = 0b0111;
|
|
packed_widths[0] = 10;
|
|
packed_widths[1] = packed_widths[2] = 11;
|
|
packed_offsets[1] = 10;
|
|
packed_offsets[2] = 21;
|
|
} break;
|
|
case xenos::VertexFormat::k_16_16: {
|
|
packed_components = 0b0011;
|
|
packed_widths[0] = packed_widths[1] = 16;
|
|
packed_offsets[1] = 16;
|
|
} break;
|
|
case xenos::VertexFormat::k_16_16_16_16: {
|
|
packed_components = 0b1111;
|
|
packed_widths[0] = packed_widths[1] = packed_widths[2] =
|
|
packed_widths[3] = 16;
|
|
packed_offsets[1] = packed_offsets[3] = 16;
|
|
packed_dwords[1] = data[1];
|
|
} break;
|
|
case xenos::VertexFormat::k_16_16_16_16_FLOAT: {
|
|
if (used_result_components & 0b1000) {
|
|
result[3] = xe::xenos_half_to_float(uint16_t(data[1] >> 16));
|
|
}
|
|
if (used_result_components & 0b0100) {
|
|
result[2] = xe::xenos_half_to_float(uint16_t(data[1]));
|
|
}
|
|
}
|
|
[[fallthrough]];
|
|
case xenos::VertexFormat::k_16_16_FLOAT: {
|
|
if (used_result_components & 0b0010) {
|
|
result[1] = xe::xenos_half_to_float(uint16_t(data[0] >> 16));
|
|
}
|
|
if (used_result_components & 0b0001) {
|
|
result[0] = xe::xenos_half_to_float(uint16_t(data[0]));
|
|
}
|
|
} break;
|
|
case xenos::VertexFormat::k_32:
|
|
case xenos::VertexFormat::k_32_32:
|
|
case xenos::VertexFormat::k_32_32_32_32: {
|
|
if (instr.is_signed()) {
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
result[i] = float(int32_t(data[i]));
|
|
}
|
|
if (instr.is_normalized()) {
|
|
if (instr.signed_rf_mode() ==
|
|
xenos::SignedRepeatingFractionMode::kNoZero) {
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
result[i] = (result[i] + 0.5f) / 2147483647.5f;
|
|
}
|
|
} else {
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
result[i] /= 2147483647.0f;
|
|
// No need to clamp to -1 if signed - the smallest value will be
|
|
// -2^23 / 2^23 due to rounding.
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
result[i] = float(data[i]);
|
|
}
|
|
if (instr.is_normalized()) {
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
result[i] /= 4294967295.0f;
|
|
}
|
|
}
|
|
}
|
|
} break;
|
|
case xenos::VertexFormat::k_32_FLOAT:
|
|
case xenos::VertexFormat::k_32_32_FLOAT:
|
|
case xenos::VertexFormat::k_32_32_32_32_FLOAT:
|
|
case xenos::VertexFormat::k_32_32_32_FLOAT: {
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
result[i] = *reinterpret_cast<const float*>(&data[i]);
|
|
}
|
|
} break;
|
|
default:
|
|
assert_unhandled_case(instr.data_format());
|
|
break;
|
|
}
|
|
|
|
packed_components &= used_result_components;
|
|
if (packed_components) {
|
|
if (instr.is_signed()) {
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
if (!(packed_components & (UINT32_C(1) << i))) {
|
|
continue;
|
|
}
|
|
uint32_t packed_width = packed_widths[i];
|
|
result[i] = float(int32_t(packed_dwords[i >> 1])
|
|
<< (32 - (packed_width + packed_offsets[i])) >>
|
|
(32 - packed_width));
|
|
}
|
|
if (instr.is_normalized()) {
|
|
if (instr.signed_rf_mode() ==
|
|
xenos::SignedRepeatingFractionMode::kNoZero) {
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
if (!(packed_components & (UINT32_C(1) << i))) {
|
|
continue;
|
|
}
|
|
result[i] = (result[i] + 0.5f) * 2.0f /
|
|
float((UINT32_C(1) << packed_widths[i]) - 1);
|
|
}
|
|
} else {
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
if (!(packed_components & (UINT32_C(1) << i))) {
|
|
continue;
|
|
}
|
|
result[i] = std::max(
|
|
-1.0f,
|
|
result[i] /
|
|
float((UINT32_C(1) << (packed_widths[i] - 1)) - 1));
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
if (!(packed_components & (UINT32_C(1) << i))) {
|
|
continue;
|
|
}
|
|
uint32_t packed_width = packed_widths[i];
|
|
result[i] = float(packed_dwords[i >> 1] &
|
|
((UINT32_C(1) << packed_widths[i]) - 1));
|
|
}
|
|
if (instr.is_normalized()) {
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
if (!(packed_components & (UINT32_C(1) << i))) {
|
|
continue;
|
|
}
|
|
result[i] /= float((UINT32_C(1) << packed_widths[i]) - 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int32_t exp_adjust = instr.exp_adjust();
|
|
if (exp_adjust) {
|
|
float exp_adjust_factor = std::ldexp(1.0f, exp_adjust);
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
result[i] *= exp_adjust_factor;
|
|
}
|
|
}
|
|
|
|
StoreFetchResult(instr.dest(), instr.is_dest_relative(), instr.dest_swizzle(),
|
|
result);
|
|
}
|
|
|
|
} // namespace gpu
|
|
} // namespace xe
|