Merge branch 'master' of https://github.com/xenia-project/xenia into canary_experimental

This commit is contained in:
Gliniak
2024-05-31 22:43:59 +02:00
38 changed files with 3073 additions and 1908 deletions
+1 -1
View File
@@ -122,7 +122,7 @@ void XmaContext::ConvertFrame(const uint8_t** samples, bool is_two_channel,
auto in = reinterpret_cast<const float*>(samples[j]);
// Raw samples sometimes aren't within [-1, 1]
float scaled_sample = xe::saturate_signed(in[i]) * scale;
float scaled_sample = xe::clamp_float(in[i], -1.0f, 1.0f) * scale;
// Convert the sample and output it in big endian.
auto sample = static_cast<int16_t>(scaled_sample);
+12 -16
View File
@@ -72,20 +72,22 @@ constexpr T round_up(T value, V multiple, bool force_non_zero = true) {
return (value + multiple - 1) / multiple * multiple;
}
// Using the same conventions as in shading languages, returning 0 for NaN.
// std::max is `a < b ? b : a`, thus in case of NaN, the first argument is
// always returned. Also -0 is not < +0, so +0 is also chosen for it.
// For NaN, returns min_value (or, if it's NaN too, max_value).
// If either of the boundaries is zero, and if the value is at that boundary or
// exceeds it, the result will have the sign of that boundary. If both
// boundaries are zero, which sign is selected among the argument signs is not
// explicitly defined.
template <typename T>
constexpr T saturate_unsigned(T value) {
return std::min(static_cast<T>(1.0f), std::max(static_cast<T>(0.0f), value));
T clamp_float(T value, T min_value, T max_value) {
float clamped_to_min = std::isgreater(value, min_value) ? value : min_value;
return std::isless(clamped_to_min, max_value) ? clamped_to_min : max_value;
}
// This diverges from the GPU NaN rules for signed normalized formats (NaN
// should be converted to 0, not to -1), but this expectation is not needed most
// of time, and cannot be met for free (unlike for 0...1 clamping).
// Using the same conventions as in shading languages, returning 0 for NaN.
// 0 is always returned as positive.
template <typename T>
constexpr T saturate_signed(T value) {
return std::min(static_cast<T>(1.0f), std::max(static_cast<T>(-1.0f), value));
T saturate(T value) {
return clamp_float(value, static_cast<T>(0.0f), static_cast<T>(1.0f));
}
// Gets the next power of two value that is greater than or equal to the given
@@ -365,12 +367,6 @@ inline uint64_t rotate_right(uint64_t v, uint8_t sh) {
}
#endif // XE_PLATFORM_WIN32
template <typename T>
T clamp(T value, T min_value, T max_value) {
const T t = value < min_value ? min_value : value;
return t > max_value ? max_value : t;
}
#if XE_ARCH_AMD64
// Utilities for SSE values.
template <int N>
+25
View File
@@ -16,12 +16,37 @@
#include <functional>
#include <string>
#include <string_view>
#include <type_traits>
#include "xenia/base/byte_order.h"
namespace xe {
namespace memory {
// For variable declarations (not return values or `this` pointer).
// Not propagated.
#define XE_RESTRICT_VAR __restrict
// Aliasing-safe bit reinterpretation.
// For more complex cases such as non-trivially-copyable types, write copying
// code respecting the requirements for them externally instead of using these
// functions.
template <typename Dst, typename Src>
void Reinterpret(Dst& XE_RESTRICT_VAR dst, const Src& XE_RESTRICT_VAR src) {
static_assert(sizeof(Dst) == sizeof(Src));
static_assert(std::is_trivially_copyable_v<Dst>);
static_assert(std::is_trivially_copyable_v<Src>);
std::memcpy(&dst, &src, sizeof(Dst));
}
template <typename Dst, typename Src>
Dst Reinterpret(const Src& XE_RESTRICT_VAR src) {
Dst dst;
Reinterpret(dst, src);
return dst;
}
#if XE_PLATFORM_ANDROID
void AndroidInitialize();
void AndroidShutdown();
+5 -4
View File
@@ -107,10 +107,11 @@ TEST_CASE("WinSystemClock <-> XSystemClock", "[clock_cast]") {
auto error2 = xsys.time_since_epoch() - wxsys.time_since_epoch();
auto error3 = wsys - wxsys;
REQUIRE(error1 < 10ms);
REQUIRE(error1 > -10ms);
REQUIRE(error2 < 10ms);
REQUIRE(error2 > -10ms);
// In AppVeyor, the difference often can be as large as roughly 16ms.
REQUIRE(error1 < 20ms);
REQUIRE(error1 > -20ms);
REQUIRE(error2 < 20ms);
REQUIRE(error2 > -20ms);
REQUIRE(error3 < duration);
REQUIRE(error3 > -duration);
}
+7 -5
View File
@@ -182,7 +182,7 @@ void DebugWindow::DrawFrame(ImGuiIO& io) {
ImVec2(kSplitterWidth, top_panes_height));
if (ImGui::IsItemActive()) {
function_pane_width += io.MouseDelta.x;
function_pane_width = xe::clamp(function_pane_width, 30.0f, FLT_MAX);
function_pane_width = xe::clamp_float(function_pane_width, 30.0f, FLT_MAX);
}
ImGui::SameLine();
ImGui::BeginChild("##source_pane",
@@ -194,7 +194,7 @@ void DebugWindow::DrawFrame(ImGuiIO& io) {
ImVec2(kSplitterWidth, top_panes_height));
if (ImGui::IsItemActive()) {
source_pane_width += io.MouseDelta.x;
source_pane_width = xe::clamp(source_pane_width, 30.0f, FLT_MAX);
source_pane_width = xe::clamp_float(source_pane_width, 30.0f, FLT_MAX);
}
ImGui::SameLine();
ImGui::BeginChild("##registers_pane",
@@ -206,7 +206,8 @@ void DebugWindow::DrawFrame(ImGuiIO& io) {
ImVec2(kSplitterWidth, top_panes_height));
if (ImGui::IsItemActive()) {
registers_pane_width += io.MouseDelta.x;
registers_pane_width = xe::clamp(registers_pane_width, 30.0f, FLT_MAX);
registers_pane_width =
xe::clamp_float(registers_pane_width, 30.0f, FLT_MAX);
}
ImGui::SameLine();
ImGui::BeginChild("##right_pane", ImVec2(0, top_panes_height), true);
@@ -234,7 +235,7 @@ void DebugWindow::DrawFrame(ImGuiIO& io) {
ImGui::InvisibleButton("##hsplitter0", ImVec2(-1, kSplitterWidth));
if (ImGui::IsItemActive()) {
bottom_panes_height -= io.MouseDelta.y;
bottom_panes_height = xe::clamp(bottom_panes_height, 30.0f, FLT_MAX);
bottom_panes_height = xe::clamp_float(bottom_panes_height, 30.0f, FLT_MAX);
}
ImGui::BeginChild("##log_pane", ImVec2(log_pane_width, bottom_panes_height),
true);
@@ -245,7 +246,8 @@ void DebugWindow::DrawFrame(ImGuiIO& io) {
ImVec2(kSplitterWidth, bottom_panes_height));
if (ImGui::IsItemActive()) {
breakpoints_pane_width -= io.MouseDelta.x;
breakpoints_pane_width = xe::clamp(breakpoints_pane_width, 30.0f, FLT_MAX);
breakpoints_pane_width =
xe::clamp_float(breakpoints_pane_width, 30.0f, FLT_MAX);
}
ImGui::SameLine();
ImGui::BeginChild("##breakpoints_pane", ImVec2(0, 0), true);
+37 -23
View File
@@ -455,9 +455,9 @@ void CommandProcessor::HandleSpecialRegisterWrite(uint32_t index,
// Scratch register writeback.
if (index >= XE_GPU_REG_SCRATCH_REG0 && index <= XE_GPU_REG_SCRATCH_REG7) {
uint32_t scratch_reg = index - XE_GPU_REG_SCRATCH_REG0;
if ((1 << scratch_reg) & regs.values[XE_GPU_REG_SCRATCH_UMSK].u32) {
if ((1 << scratch_reg) & regs.values[XE_GPU_REG_SCRATCH_UMSK]) {
// Enabled - write to address.
uint32_t scratch_addr = regs.values[XE_GPU_REG_SCRATCH_ADDR].u32;
uint32_t scratch_addr = regs.values[XE_GPU_REG_SCRATCH_ADDR];
uint32_t mem_addr = scratch_addr + (scratch_reg * 4);
xe::store_and_swap<uint32_t>(memory_->TranslatePhysical(mem_addr), value);
}
@@ -467,7 +467,7 @@ void CommandProcessor::HandleSpecialRegisterWrite(uint32_t index,
// This will block the command processor the next time it WAIT_MEM_REGs
// and allow us to synchronize the memory.
case XE_GPU_REG_COHER_STATUS_HOST: {
regs.values[index].u32 |= UINT32_C(0x80000000);
regs.values[index] |= UINT32_C(0x80000000);
} break;
case XE_GPU_REG_DC_LUT_RW_INDEX: {
@@ -478,12 +478,12 @@ void CommandProcessor::HandleSpecialRegisterWrite(uint32_t index,
case XE_GPU_REG_DC_LUT_SEQ_COLOR: {
// Should be in the 256-entry table writing mode.
assert_zero(regs[XE_GPU_REG_DC_LUT_RW_MODE].u32 & 0b1);
auto& gamma_ramp_rw_index = regs.Get<reg::DC_LUT_RW_INDEX>();
assert_zero(regs[XE_GPU_REG_DC_LUT_RW_MODE] & 0b1);
auto gamma_ramp_rw_index = regs.Get<reg::DC_LUT_RW_INDEX>();
// DC_LUT_SEQ_COLOR is in the red, green, blue order, but the write
// enable mask is blue, green, red.
bool write_gamma_ramp_component =
(regs[XE_GPU_REG_DC_LUT_WRITE_EN_MASK].u32 &
(regs[XE_GPU_REG_DC_LUT_WRITE_EN_MASK] &
(UINT32_C(1) << (2 - gamma_ramp_rw_component_))) != 0;
if (write_gamma_ramp_component) {
reg::DC_LUT_30_COLOR& gamma_ramp_entry =
@@ -505,7 +505,11 @@ void CommandProcessor::HandleSpecialRegisterWrite(uint32_t index,
}
if (++gamma_ramp_rw_component_ >= 3) {
gamma_ramp_rw_component_ = 0;
++gamma_ramp_rw_index.rw_index;
reg::DC_LUT_RW_INDEX new_gamma_ramp_rw_index = gamma_ramp_rw_index;
++new_gamma_ramp_rw_index.rw_index;
WriteRegister(
XE_GPU_REG_DC_LUT_RW_INDEX,
xe::memory::Reinterpret<uint32_t>(new_gamma_ramp_rw_index));
}
if (write_gamma_ramp_component) {
OnGammaRamp256EntryTableValueWritten();
@@ -514,14 +518,14 @@ void CommandProcessor::HandleSpecialRegisterWrite(uint32_t index,
case XE_GPU_REG_DC_LUT_PWL_DATA: {
// Should be in the PWL writing mode.
assert_not_zero(regs[XE_GPU_REG_DC_LUT_RW_MODE].u32 & 0b1);
auto& gamma_ramp_rw_index = regs.Get<reg::DC_LUT_RW_INDEX>();
assert_not_zero(regs[XE_GPU_REG_DC_LUT_RW_MODE] & 0b1);
auto gamma_ramp_rw_index = regs.Get<reg::DC_LUT_RW_INDEX>();
// Bit 7 of the index is ignored for PWL.
uint32_t gamma_ramp_rw_index_pwl = gamma_ramp_rw_index.rw_index & 0x7F;
// DC_LUT_PWL_DATA is likely in the red, green, blue order because
// DC_LUT_SEQ_COLOR is, but the write enable mask is blue, green, red.
bool write_gamma_ramp_component =
(regs[XE_GPU_REG_DC_LUT_WRITE_EN_MASK].u32 &
(regs[XE_GPU_REG_DC_LUT_WRITE_EN_MASK] &
(UINT32_C(1) << (2 - gamma_ramp_rw_component_))) != 0;
if (write_gamma_ramp_component) {
reg::DC_LUT_PWL_DATA& gamma_ramp_entry =
@@ -534,13 +538,17 @@ void CommandProcessor::HandleSpecialRegisterWrite(uint32_t index,
}
if (++gamma_ramp_rw_component_ >= 3) {
gamma_ramp_rw_component_ = 0;
reg::DC_LUT_RW_INDEX new_gamma_ramp_rw_index = gamma_ramp_rw_index;
// TODO(Triang3l): Should this increase beyond 7 bits for PWL?
// Direct3D 9 explicitly sets rw_index to 0x80 after writing the last
// PWL entry. However, the DC_LUT_RW_INDEX documentation says that for
// PWL, the bit 7 is ignored.
gamma_ramp_rw_index.rw_index =
new_gamma_ramp_rw_index.rw_index =
(gamma_ramp_rw_index.rw_index & ~UINT32_C(0x7F)) |
((gamma_ramp_rw_index_pwl + 1) & 0x7F);
WriteRegister(
XE_GPU_REG_DC_LUT_RW_INDEX,
xe::memory::Reinterpret<uint32_t>(new_gamma_ramp_rw_index));
}
if (write_gamma_ramp_component) {
OnGammaRampPWLValueWritten();
@@ -549,10 +557,10 @@ void CommandProcessor::HandleSpecialRegisterWrite(uint32_t index,
case XE_GPU_REG_DC_LUT_30_COLOR: {
// Should be in the 256-entry table writing mode.
assert_zero(regs[XE_GPU_REG_DC_LUT_RW_MODE].u32 & 0b1);
auto& gamma_ramp_rw_index = regs.Get<reg::DC_LUT_RW_INDEX>();
assert_zero(regs[XE_GPU_REG_DC_LUT_RW_MODE] & 0b1);
auto gamma_ramp_rw_index = regs.Get<reg::DC_LUT_RW_INDEX>();
uint32_t gamma_ramp_write_enable_mask =
regs[XE_GPU_REG_DC_LUT_WRITE_EN_MASK].u32 & 0b111;
regs[XE_GPU_REG_DC_LUT_WRITE_EN_MASK] & 0b111;
if (gamma_ramp_write_enable_mask) {
reg::DC_LUT_30_COLOR& gamma_ramp_entry =
gamma_ramp_256_entry_table_[gamma_ramp_rw_index.rw_index];
@@ -567,11 +575,16 @@ void CommandProcessor::HandleSpecialRegisterWrite(uint32_t index,
gamma_ramp_entry.color_10_red = gamma_ramp_value.color_10_red;
}
}
++gamma_ramp_rw_index.rw_index;
// TODO(Triang3l): Should this reset the component write index? If this
// increase is assumed to behave like a full DC_LUT_RW_INDEX write, it
// probably should.
// probably should. Currently this also calls WriteRegister for
// DC_LUT_RW_INDEX, which resets gamma_ramp_rw_component_ as well.
gamma_ramp_rw_component_ = 0;
reg::DC_LUT_RW_INDEX new_gamma_ramp_rw_index = gamma_ramp_rw_index;
++new_gamma_ramp_rw_index.rw_index;
WriteRegister(
XE_GPU_REG_DC_LUT_RW_INDEX,
xe::memory::Reinterpret<uint32_t>(new_gamma_ramp_rw_index));
if (gamma_ramp_write_enable_mask) {
OnGammaRamp256EntryTableValueWritten();
}
@@ -583,7 +596,7 @@ void CommandProcessor::WriteRegister(uint32_t index, uint32_t value) {
// chrispy: rearrange check order, place set after checks
if (XE_LIKELY(index < RegisterFile::kRegisterCount)) {
register_file_->values[index].u32 = value;
register_file_->values[index] = value;
// quick pre-test
// todo: figure out just how unlikely this is. if very (it ought to be,
@@ -708,10 +721,11 @@ void CommandProcessor::MakeCoherent() {
// https://web.archive.org/web/20160711162346/https://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2013/10/R6xx_R7xx_3D.pdf
// https://cgit.freedesktop.org/xorg/driver/xf86-video-radeonhd/tree/src/r6xx_accel.c?id=3f8b6eccd9dba116cc4801e7f80ce21a879c67d2#n454
RegisterFile* regs = register_file_;
auto& status_host = regs->Get<reg::COHER_STATUS_HOST>();
auto base_host = regs->values[XE_GPU_REG_COHER_BASE_HOST].u32;
auto size_host = regs->values[XE_GPU_REG_COHER_SIZE_HOST].u32;
volatile uint32_t* regs_volatile = register_file_->values;
auto status_host = xe::memory::Reinterpret<reg::COHER_STATUS_HOST>(
uint32_t(regs_volatile[XE_GPU_REG_COHER_STATUS_HOST]));
uint32_t base_host = regs_volatile[XE_GPU_REG_COHER_BASE_HOST];
uint32_t size_host = regs_volatile[XE_GPU_REG_COHER_SIZE_HOST];
if (!status_host.status) {
return;
@@ -731,7 +745,7 @@ void CommandProcessor::MakeCoherent() {
base_host + size_host, size_host, action);
// Mark coherent.
status_host.status = 0;
regs_volatile[XE_GPU_REG_COHER_STATUS_HOST] = 0;
}
void CommandProcessor::PrepareForWait() { trace_writer_.Flush(); }
@@ -752,4 +766,4 @@ void CommandProcessor::InitializeTrace() {
#define COMMAND_PROCESSOR CommandProcessor
#include "pm4_command_processor_implement.h"
} // namespace gpu
} // namespace xe
} // namespace xe
+43 -47
View File
@@ -1768,7 +1768,7 @@ void D3D12CommandProcessor::WriteRegisterForceinline(uint32_t index,
__m128i is_above_lower = _mm_cmpgt_epi16(to_rangecheck, lower_bounds);
__m128i is_below_upper = _mm_cmplt_epi16(to_rangecheck, upper_bounds);
__m128i is_within_range = _mm_and_si128(is_above_lower, is_below_upper);
register_file_->values[index].u32 = value;
register_file_->values[index] = value;
uint32_t movmask = static_cast<uint32_t>(_mm_movemask_epi8(is_within_range));
@@ -2047,7 +2047,7 @@ void D3D12CommandProcessor::WritePossiblySpecialRegistersFromMem(
for (uint32_t index = start_index; index < end; ++index, ++base) {
uint32_t value = xe::load_and_swap<uint32_t>(base);
register_file_->values[index].u32 = value;
register_file_->values[index] = value;
unsigned expr = 0;
@@ -2780,8 +2780,8 @@ bool D3D12CommandProcessor::IssueDraw(xenos::PrimitiveType primitive_type,
while (xe::bit_scan_forward(vfetch_bits_remaining, &j)) {
vfetch_bits_remaining = xe::clear_lowest_bit(vfetch_bits_remaining);
uint32_t vfetch_index = i * 32 + j;
const auto& vfetch_constant = regs.Get<xenos::xe_gpu_vertex_fetch_t>(
XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0 + vfetch_index * 2);
xenos::xe_gpu_vertex_fetch_t vfetch_constant =
regs.GetVertexFetch(vfetch_index);
switch (vfetch_constant.type) {
case xenos::FetchConstantType::kVertex:
break;
@@ -3554,10 +3554,10 @@ void D3D12CommandProcessor::UpdateFixedFunctionState(
// Blend factor.
float blend_factor[] = {
regs[XE_GPU_REG_RB_BLEND_RED].f32,
regs[XE_GPU_REG_RB_BLEND_GREEN].f32,
regs[XE_GPU_REG_RB_BLEND_BLUE].f32,
regs[XE_GPU_REG_RB_BLEND_ALPHA].f32,
regs.Get<float>(XE_GPU_REG_RB_BLEND_RED),
regs.Get<float>(XE_GPU_REG_RB_BLEND_GREEN),
regs.Get<float>(XE_GPU_REG_RB_BLEND_BLUE),
regs.Get<float>(XE_GPU_REG_RB_BLEND_ALPHA),
};
// std::memcmp instead of != so in case of NaN, every draw won't be
// invalidating it.
@@ -3599,7 +3599,7 @@ XE_NOINLINE void D3D12CommandProcessor::UpdateSystemConstantValues_Impl(
auto pa_cl_clip_cntl = regs.Get<reg::PA_CL_CLIP_CNTL>();
auto pa_cl_vte_cntl = regs.Get<reg::PA_CL_VTE_CNTL>();
auto pa_su_sc_mode_cntl = regs.Get<reg::PA_SU_SC_MODE_CNTL>();
float rb_alpha_ref = regs[XE_GPU_REG_RB_ALPHA_REF].f32;
auto rb_alpha_ref = regs.Get<float>(XE_GPU_REG_RB_ALPHA_REF);
auto rb_colorcontrol = regs.Get<reg::RB_COLORCONTROL>();
auto rb_depth_info = regs.Get<reg::RB_DEPTH_INFO>();
auto rb_stencilrefmask = regs.Get<reg::RB_STENCILREFMASK>();
@@ -3753,10 +3753,10 @@ XE_NOINLINE void D3D12CommandProcessor::UpdateSystemConstantValues_Impl(
// Tessellation factor range, plus 1.0 according to the images in
// https://www.slideshare.net/blackdevilvikas/next-generation-graphics-programming-on-xbox-360
float tessellation_factor_min =
regs[XE_GPU_REG_VGT_HOS_MIN_TESS_LEVEL].f32 + 1.0f;
float tessellation_factor_max =
regs[XE_GPU_REG_VGT_HOS_MAX_TESS_LEVEL].f32 + 1.0f;
auto tessellation_factor_min =
regs.Get<float>(XE_GPU_REG_VGT_HOS_MIN_TESS_LEVEL) + 1.0f;
auto tessellation_factor_max =
regs.Get<float>(XE_GPU_REG_VGT_HOS_MAX_TESS_LEVEL) + 1.0f;
update_dirty_floatmask(system_constants_.tessellation_factor_range_min,
tessellation_factor_min);
@@ -3804,12 +3804,12 @@ XE_NOINLINE void D3D12CommandProcessor::UpdateSystemConstantValues_Impl(
&user_clip_plane_index)) {
user_clip_planes_remaining =
xe::clear_lowest_bit(user_clip_planes_remaining);
const float* user_clip_plane =
&regs[XE_GPU_REG_PA_CL_UCP_0_X + user_clip_plane_index * 4].f32;
if (std::memcmp(user_clip_plane_write_ptr, user_clip_plane,
const void* user_clip_plane_regs =
&regs[XE_GPU_REG_PA_CL_UCP_0_X + user_clip_plane_index * 4];
if (std::memcmp(user_clip_plane_write_ptr, user_clip_plane_regs,
4 * sizeof(float))) {
dirty = true;
std::memcpy(user_clip_plane_write_ptr, user_clip_plane,
std::memcpy(user_clip_plane_write_ptr, user_clip_plane_regs,
4 * sizeof(float));
}
user_clip_plane_write_ptr += 4;
@@ -3974,9 +3974,8 @@ XE_NOINLINE void D3D12CommandProcessor::UpdateSystemConstantValues_Impl(
color_exp_bias -= 5;
}
}
float color_exp_bias_scale;
*reinterpret_cast<int32_t*>(&color_exp_bias_scale) =
0x3F800000 + (color_exp_bias << 23);
auto color_exp_bias_scale = xe::memory::Reinterpret<float>(
int32_t(0x3F800000 + (color_exp_bias << 23)));
update_dirty_floatmask(system_constants_.color_exp_bias[i],
color_exp_bias_scale);
@@ -4028,7 +4027,7 @@ XE_NOINLINE void D3D12CommandProcessor::UpdateSystemConstantValues_Impl(
#endif
uint32_t blend_factors_ops =
regs[reg::RB_BLENDCONTROL::rt_register_indices[i]].u32 & 0x1FFF1FFF;
regs[reg::RB_BLENDCONTROL::rt_register_indices[i]] & 0x1FFF1FFF;
update_dirty_uint32_cmp(system_constants_.edram_rt_blend_factors_ops[i],
blend_factors_ops);
@@ -4060,22 +4059,22 @@ XE_NOINLINE void D3D12CommandProcessor::UpdateSystemConstantValues_Impl(
if (primitive_polygonal) {
if (pa_su_sc_mode_cntl.poly_offset_front_enable) {
poly_offset_front_scale =
regs[XE_GPU_REG_PA_SU_POLY_OFFSET_FRONT_SCALE].f32;
regs.Get<float>(XE_GPU_REG_PA_SU_POLY_OFFSET_FRONT_SCALE);
poly_offset_front_offset =
regs[XE_GPU_REG_PA_SU_POLY_OFFSET_FRONT_OFFSET].f32;
regs.Get<float>(XE_GPU_REG_PA_SU_POLY_OFFSET_FRONT_OFFSET);
}
if (pa_su_sc_mode_cntl.poly_offset_back_enable) {
poly_offset_back_scale =
regs[XE_GPU_REG_PA_SU_POLY_OFFSET_BACK_SCALE].f32;
regs.Get<float>(XE_GPU_REG_PA_SU_POLY_OFFSET_BACK_SCALE);
poly_offset_back_offset =
regs[XE_GPU_REG_PA_SU_POLY_OFFSET_BACK_OFFSET].f32;
regs.Get<float>(XE_GPU_REG_PA_SU_POLY_OFFSET_BACK_OFFSET);
}
} else {
if (pa_su_sc_mode_cntl.poly_offset_para_enable) {
poly_offset_front_scale =
regs[XE_GPU_REG_PA_SU_POLY_OFFSET_FRONT_SCALE].f32;
regs.Get<float>(XE_GPU_REG_PA_SU_POLY_OFFSET_FRONT_SCALE);
poly_offset_front_offset =
regs[XE_GPU_REG_PA_SU_POLY_OFFSET_FRONT_OFFSET].f32;
regs.Get<float>(XE_GPU_REG_PA_SU_POLY_OFFSET_FRONT_OFFSET);
poly_offset_back_scale = poly_offset_front_scale;
poly_offset_back_offset = poly_offset_front_offset;
}
@@ -4153,26 +4152,26 @@ XE_NOINLINE void D3D12CommandProcessor::UpdateSystemConstantValues_Impl(
}
}
update_dirty_floatmask(system_constants_.edram_blend_constant[0],
regs[XE_GPU_REG_RB_BLEND_RED].f32);
regs.Get<float>(XE_GPU_REG_RB_BLEND_RED));
system_constants_.edram_blend_constant[0] =
regs[XE_GPU_REG_RB_BLEND_RED].f32;
regs.Get<float>(XE_GPU_REG_RB_BLEND_RED);
update_dirty_floatmask(system_constants_.edram_blend_constant[1],
regs[XE_GPU_REG_RB_BLEND_GREEN].f32);
regs.Get<float>(XE_GPU_REG_RB_BLEND_GREEN));
system_constants_.edram_blend_constant[1] =
regs[XE_GPU_REG_RB_BLEND_GREEN].f32;
regs.Get<float>(XE_GPU_REG_RB_BLEND_GREEN);
update_dirty_floatmask(system_constants_.edram_blend_constant[2],
regs[XE_GPU_REG_RB_BLEND_BLUE].f32);
regs.Get<float>(XE_GPU_REG_RB_BLEND_BLUE));
system_constants_.edram_blend_constant[2] =
regs[XE_GPU_REG_RB_BLEND_BLUE].f32;
regs.Get<float>(XE_GPU_REG_RB_BLEND_BLUE);
update_dirty_floatmask(system_constants_.edram_blend_constant[3],
regs[XE_GPU_REG_RB_BLEND_ALPHA].f32);
regs.Get<float>(XE_GPU_REG_RB_BLEND_ALPHA));
system_constants_.edram_blend_constant[3] =
regs[XE_GPU_REG_RB_BLEND_ALPHA].f32;
regs.Get<float>(XE_GPU_REG_RB_BLEND_ALPHA);
}
dirty |= ArchFloatMaskSignbit(dirty_float_mask);
@@ -4266,10 +4265,10 @@ bool D3D12CommandProcessor::UpdateBindings(const D3D12Shader* vertex_shader,
// These are the constant base addresses/ranges for shaders.
// We have these hardcoded right now cause nothing seems to differ on the Xbox
// 360 (however, OpenGL ES on Adreno 200 on Android has different ranges).
assert_true(regs[XE_GPU_REG_SQ_VS_CONST].u32 == 0x000FF000 ||
regs[XE_GPU_REG_SQ_VS_CONST].u32 == 0x00000000);
assert_true(regs[XE_GPU_REG_SQ_PS_CONST].u32 == 0x000FF100 ||
regs[XE_GPU_REG_SQ_PS_CONST].u32 == 0x00000000);
assert_true(regs[XE_GPU_REG_SQ_VS_CONST] == 0x000FF000 ||
regs[XE_GPU_REG_SQ_VS_CONST] == 0x00000000);
assert_true(regs[XE_GPU_REG_SQ_PS_CONST] == 0x000FF100 ||
regs[XE_GPU_REG_SQ_PS_CONST] == 0x00000000);
// Check if the float constant layout is still the same and get the counts.
const Shader::ConstantRegisterMap& float_constant_map_vertex =
vertex_shader->constant_register_map();
@@ -4344,8 +4343,7 @@ bool D3D12CommandProcessor::UpdateBindings(const D3D12Shader* vertex_shader,
xe::clear_lowest_bit(float_constant_map_entry);
std::memcpy(float_constants,
&regs[XE_GPU_REG_SHADER_CONSTANT_000_X + (i << 8) +
(float_constant_index << 2)]
.f32,
(float_constant_index << 2)],
4 * sizeof(float));
float_constants += 4 * sizeof(float);
}
@@ -4376,8 +4374,7 @@ bool D3D12CommandProcessor::UpdateBindings(const D3D12Shader* vertex_shader,
xe::clear_lowest_bit(float_constant_map_entry);
std::memcpy(float_constants,
&regs[XE_GPU_REG_SHADER_CONSTANT_256_X + (i << 8) +
(float_constant_index << 2)]
.f32,
(float_constant_index << 2)],
4 * sizeof(float));
float_constants += 4 * sizeof(float);
}
@@ -4397,8 +4394,7 @@ bool D3D12CommandProcessor::UpdateBindings(const D3D12Shader* vertex_shader,
return false;
}
xe::smallcpy_const<kBoolLoopConstantsSize>(
bool_loop_constants,
&regs[XE_GPU_REG_SHADER_CONSTANT_BOOL_000_031].u32);
bool_loop_constants, &regs[XE_GPU_REG_SHADER_CONSTANT_BOOL_000_031]);
cbuffer_binding_bool_loop_.up_to_date = true;
current_graphics_root_up_to_date_ &=
@@ -4414,7 +4410,7 @@ bool D3D12CommandProcessor::UpdateBindings(const D3D12Shader* vertex_shader,
return false;
}
xe::smallcpy_const<kFetchConstantsSize>(
fetch_constants, &regs[XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0].u32);
fetch_constants, &regs[XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0]);
cbuffer_binding_fetch_.up_to_date = true;
current_graphics_root_up_to_date_ &=
@@ -5152,4 +5148,4 @@ void D3D12CommandProcessor::WriteGammaRampSRV(
#undef COMMAND_PROCESSOR
} // namespace d3d12
} // namespace gpu
} // namespace xe
} // namespace xe
+3 -4
View File
@@ -679,8 +679,8 @@ void D3D12TextureCache::PrefetchSamplerParameters(
D3D12TextureCache::SamplerParameters D3D12TextureCache::GetSamplerParameters(
const D3D12Shader::SamplerBinding& binding) const {
const auto& regs = register_file();
const auto& fetch = regs.Get<xenos::xe_gpu_texture_fetch_t>(
XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0 + binding.fetch_constant * 6);
xenos::xe_gpu_texture_fetch_t fetch =
regs.GetTextureFetch(binding.fetch_constant);
SamplerParameters parameters;
@@ -1160,8 +1160,7 @@ ID3D12Resource* D3D12TextureCache::RequestSwapTexture(
D3D12_SHADER_RESOURCE_VIEW_DESC& srv_desc_out,
xenos::TextureFormat& format_out) {
const auto& regs = register_file();
const auto& fetch = regs.Get<xenos::xe_gpu_texture_fetch_t>(
XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0);
xenos::xe_gpu_texture_fetch_t fetch = regs.GetTextureFetch(0);
TextureKey key;
BindingInfoFromFetchConstant(fetch, key, nullptr);
if (!key.is_valid || key.base_page == 0 ||
+22 -20
View File
@@ -15,6 +15,7 @@
#include "xenia/base/assert.h"
#include "xenia/base/cvar.h"
#include "xenia/base/memory.h"
#include "xenia/base/profiling.h"
#include "xenia/gpu/registers.h"
#include "xenia/gpu/ucode.h"
@@ -67,7 +68,7 @@ void DrawExtentEstimator::PositionYExportSink::Export(
point_size_ = value[0];
}
if (value_mask & 0b0100) {
vertex_kill_ = *reinterpret_cast<const uint32_t*>(&value[2]);
vertex_kill_ = xe::memory::Reinterpret<uint32_t>(value[2]);
}
}
}
@@ -110,7 +111,7 @@ uint32_t DrawExtentEstimator::EstimateVertexMaxY(const Shader& vertex_shader) {
xenos::Endian index_endian = vgt_dma_size.swap_mode;
if (vgt_draw_initiator.source_select == xenos::SourceSelect::kDMA) {
xenos::IndexFormat index_format = vgt_draw_initiator.index_size;
uint32_t index_buffer_base = regs[XE_GPU_REG_VGT_DMA_BASE].u32;
uint32_t index_buffer_base = regs[XE_GPU_REG_VGT_DMA_BASE];
uint32_t index_buffer_read_count =
std::min(uint32_t(vgt_draw_initiator.num_indices),
uint32_t(vgt_dma_size.num_words));
@@ -145,21 +146,22 @@ uint32_t DrawExtentEstimator::EstimateVertexMaxY(const Shader& vertex_shader) {
auto pa_cl_vte_cntl = regs.Get<reg::PA_CL_VTE_CNTL>();
float viewport_y_scale = pa_cl_vte_cntl.vport_y_scale_ena
? regs[XE_GPU_REG_PA_CL_VPORT_YSCALE].f32
? regs.Get<float>(XE_GPU_REG_PA_CL_VPORT_YSCALE)
: 1.0f;
float viewport_y_offset = pa_cl_vte_cntl.vport_y_offset_ena
? regs[XE_GPU_REG_PA_CL_VPORT_YOFFSET].f32
: 0.0f;
float viewport_y_offset =
pa_cl_vte_cntl.vport_y_offset_ena
? regs.Get<float>(XE_GPU_REG_PA_CL_VPORT_YOFFSET)
: 0.0f;
int32_t point_vertex_min_diameter_float = 0;
int32_t point_vertex_max_diameter_float = 0;
float point_constant_radius_y = 0.0f;
if (vgt_draw_initiator.prim_type == xenos::PrimitiveType::kPointList) {
auto pa_su_point_minmax = regs.Get<reg::PA_SU_POINT_MINMAX>();
*reinterpret_cast<float*>(&point_vertex_min_diameter_float) =
float(pa_su_point_minmax.min_size) * (2.0f / 16.0f);
*reinterpret_cast<float*>(&point_vertex_max_diameter_float) =
float(pa_su_point_minmax.max_size) * (2.0f / 16.0f);
point_vertex_min_diameter_float = xe::memory::Reinterpret<int32_t>(
float(pa_su_point_minmax.min_size) * (2.0f / 16.0f));
point_vertex_max_diameter_float = xe::memory::Reinterpret<int32_t>(
float(pa_su_point_minmax.max_size) * (2.0f / 16.0f));
point_constant_radius_y =
float(regs.Get<reg::PA_SU_POINT_SIZE>().height) * (1.0f / 16.0f);
}
@@ -224,12 +226,13 @@ uint32_t DrawExtentEstimator::EstimateVertexMaxY(const Shader& vertex_shader) {
// Vertex-specified diameter. Clamped effectively as a signed integer in
// the hardware, -NaN, -Infinity ... -0 to the minimum, +Infinity, +NaN
// to the maximum.
point_radius_y = position_y_export_sink.point_size().value();
*reinterpret_cast<int32_t*>(&point_radius_y) = std::min(
point_vertex_max_diameter_float,
std::max(point_vertex_min_diameter_float,
*reinterpret_cast<const int32_t*>(&point_radius_y)));
point_radius_y *= 0.5f;
point_radius_y =
0.5f *
xe::memory::Reinterpret<float>(std::min(
point_vertex_max_diameter_float,
std::max(point_vertex_min_diameter_float,
xe::memory::Reinterpret<int32_t>(
position_y_export_sink.point_size().value()))));
} else {
// Constant radius.
point_radius_y = point_constant_radius_y;
@@ -329,7 +332,7 @@ uint32_t DrawExtentEstimator::EstimateMaxY(bool try_to_estimate_vertex_max_y,
float window_y_offset_f = float(window_y_offset);
float yoffset = regs[XE_GPU_REG_PA_CL_VPORT_YOFFSET].f32;
float yoffset = regs.Get<float>(XE_GPU_REG_PA_CL_VPORT_YOFFSET);
// First calculate all the integer.0 or integer.5 offsetting exactly at full
// precision.
@@ -347,11 +350,10 @@ uint32_t DrawExtentEstimator::EstimateMaxY(bool try_to_estimate_vertex_max_y,
sm3 = yoffset;
}
sm4 = pa_cl_vte_cntl.vport_y_scale_ena
? std::abs(regs[XE_GPU_REG_PA_CL_VPORT_YSCALE].f32)
? std::abs(regs.Get<float>(XE_GPU_REG_PA_CL_VPORT_YSCALE))
: 1.0f;
viewport_bottom = sm1 + sm2 + sm3 + sm4;
// Using floor, or, rather, truncation (because maxing with zero anyway)
// similar to how viewport scissoring behaves on real AMD, Intel and Nvidia
// GPUs on Direct3D 12 (but not WARP), also like in
@@ -366,4 +368,4 @@ uint32_t DrawExtentEstimator::EstimateMaxY(bool try_to_estimate_vertex_max_y,
}
} // namespace gpu
} // namespace xe
} // namespace xe
+36 -39
View File
@@ -9,8 +9,6 @@
#include "xenia/gpu/draw_util.h"
#include <cstring>
#include "xenia/base/cvar.h"
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
@@ -93,22 +91,21 @@ void GetPreferredFacePolygonOffset(const RegisterFile& regs,
// ones that are rendered (except for shadow volumes).
if (pa_su_sc_mode_cntl.poly_offset_front_enable &&
!pa_su_sc_mode_cntl.cull_front) {
scale = regs[XE_GPU_REG_PA_SU_POLY_OFFSET_FRONT_SCALE].f32;
offset = regs[XE_GPU_REG_PA_SU_POLY_OFFSET_FRONT_OFFSET].f32;
scale = regs.Get<float>(XE_GPU_REG_PA_SU_POLY_OFFSET_FRONT_SCALE);
offset = regs.Get<float>(XE_GPU_REG_PA_SU_POLY_OFFSET_FRONT_OFFSET);
scale = roundToNearestOrderOfMagnitude(scale);
}
if (pa_su_sc_mode_cntl.poly_offset_back_enable &&
!pa_su_sc_mode_cntl.cull_back && !scale && !offset) {
scale = regs[XE_GPU_REG_PA_SU_POLY_OFFSET_BACK_SCALE].f32;
offset = regs[XE_GPU_REG_PA_SU_POLY_OFFSET_BACK_OFFSET].f32;
scale = regs.Get<float>(XE_GPU_REG_PA_SU_POLY_OFFSET_BACK_SCALE);
offset = regs.Get<float>(XE_GPU_REG_PA_SU_POLY_OFFSET_BACK_OFFSET);
}
} else {
// Non-triangle primitives use the front offset, but it's toggled via
// poly_offset_para_enable.
if (pa_su_sc_mode_cntl.poly_offset_para_enable) {
scale = regs[XE_GPU_REG_PA_SU_POLY_OFFSET_FRONT_SCALE].f32;
offset = regs[XE_GPU_REG_PA_SU_POLY_OFFSET_FRONT_OFFSET].f32;
scale = regs.Get<float>(XE_GPU_REG_PA_SU_POLY_OFFSET_FRONT_SCALE);
offset = regs.Get<float>(XE_GPU_REG_PA_SU_POLY_OFFSET_FRONT_OFFSET);
}
}
scale_out = scale;
@@ -143,7 +140,7 @@ bool IsPixelShaderNeededWithRasterization(const Shader& shader,
}
// Check if a color target is actually written.
uint32_t rb_color_mask = regs[XE_GPU_REG_RB_COLOR_MASK].u32;
uint32_t rb_color_mask = regs[XE_GPU_REG_RB_COLOR_MASK];
uint32_t rts_remaining = shader.writes_color_targets();
uint32_t rt_index;
while (xe::bit_scan_forward(rts_remaining, &rt_index)) {
@@ -306,7 +303,6 @@ void GetHostViewportInfo(GetViewportInfoArgs* XE_RESTRICT args,
// Obtain the original viewport values in a normalized way.
float scale_xy[] = {
pa_cl_vte_cntl.vport_x_scale_ena ? args->PA_CL_VPORT_XSCALE : 1.0f,
pa_cl_vte_cntl.vport_y_scale_ena ? args->PA_CL_VPORT_YSCALE : 1.0f,
};
@@ -392,16 +388,11 @@ void GetHostViewportInfo(GetViewportInfoArgs* XE_RESTRICT args,
float offset_axis = offset_base_xy[i] + offset_add_xy[i];
float scale_axis = scale_xy[i];
float scale_axis_abs = std::abs(scale_xy[i]);
float axis_0 = offset_axis - scale_axis_abs;
float axis_1 = offset_axis + scale_axis_abs;
float axis_max_unscaled_float = float(xy_max_unscaled[i]);
// max(0.0f, xy) drops NaN and < 0 - max picks the first argument in the
// !(a < b) case (always for NaN), min as float (axis_max_unscaled_float
// is well below 2^24) to safely drop very large values.
uint32_t axis_0_int =
uint32_t(std::min(axis_max_unscaled_float, std::max(0.0f, axis_0)));
uint32_t axis_1_int =
uint32_t(std::min(axis_max_unscaled_float, std::max(0.0f, axis_1)));
uint32_t axis_0_int = uint32_t(xe::clamp_float(
offset_axis - scale_axis_abs, 0.0f, axis_max_unscaled_float));
uint32_t axis_1_int = uint32_t(xe::clamp_float(
offset_axis + scale_axis_abs, 0.0f, axis_max_unscaled_float));
uint32_t axis_extent_int = axis_1_int - axis_0_int;
viewport_info_out.xy_offset[i] = axis_0_int * axis_resolution_scale;
viewport_info_out.xy_extent[i] = axis_extent_int * axis_resolution_scale;
@@ -507,8 +498,8 @@ void GetHostViewportInfo(GetViewportInfoArgs* XE_RESTRICT args,
// extension. But cases when this really matters are yet to be found -
// trying to fix this will result in more correct depth values, but
// incorrect clipping.
z_min = xe::saturate_unsigned(host_clip_offset_z);
z_max = xe::saturate_unsigned(host_clip_offset_z + host_clip_scale_z);
z_min = xe::saturate(host_clip_offset_z);
z_max = xe::saturate(host_clip_offset_z + host_clip_scale_z);
// Direct3D 12 doesn't allow reverse depth range - on some drivers it
// works, on some drivers it doesn't, actually, but it was never
// explicitly allowed by the specification.
@@ -730,7 +721,7 @@ uint32_t GetNormalizedColorMask(const RegisterFile& regs,
return 0;
}
uint32_t normalized_color_mask = 0;
uint32_t rb_color_mask = regs[XE_GPU_REG_RB_COLOR_MASK].u32;
uint32_t rb_color_mask = regs[XE_GPU_REG_RB_COLOR_MASK];
for (uint32_t i = 0; i < xenos::kMaxColorRenderTargets; ++i) {
// Exclude the render targets not statically written to by the pixel shader.
// If the shader doesn't write to a render target, it shouldn't be written
@@ -776,10 +767,16 @@ void AddMemExportRanges(const RegisterFile& regs, const Shader& shader,
? regs.Get<reg::SQ_VS_CONST>().base
: regs.Get<reg::SQ_PS_CONST>().base;
for (uint32_t constant_index : shader.memexport_stream_constants()) {
const auto& stream = regs.Get<xenos::xe_gpu_memexport_stream_t>(
XE_GPU_REG_SHADER_CONSTANT_000_X +
(float_constants_base + constant_index) * 4);
if (!stream.index_count) {
xenos::xe_gpu_memexport_stream_t stream =
regs.GetMemExportStream(float_constants_base + constant_index);
// Safety checks for stream constants potentially not set up if the export
// isn't done on the control flow path taken by the shader (not checking the
// Y component because the index is more likely to be constructed
// arbitrarily).
// The hardware validates the upper bits of eA according to the
// IPR2015-00325 sequencer specification.
if (stream.const_0x1 != 0x1 || stream.const_0x4b0 != 0x4B0 ||
stream.const_0x96 != 0x96 || !stream.index_count) {
continue;
}
const FormatInfo& format_info =
@@ -821,7 +818,7 @@ void AddMemExportRanges(const RegisterFile& regs, const Shader& shader,
}
// Add a new range if haven't expanded an existing one.
if (!range_reused) {
ranges_out.emplace_back(stream.base_address, stream_size_bytes);
ranges_out.emplace_back(uint32_t(stream.base_address), stream_size_bytes);
}
}
}
@@ -943,8 +940,7 @@ bool GetResolveInfo(const RegisterFile& regs, const Memory& memory,
// Get the extent of pixels covered by the resolve rectangle, according to the
// top-left rasterization rule.
// D3D9 HACK: Vertices to use are always in vf0, and are written by the CPU.
auto fetch = regs.Get<xenos::xe_gpu_vertex_fetch_t>(
XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0);
xenos::xe_gpu_vertex_fetch_t fetch = regs.GetVertexFetch(0);
if (fetch.type != xenos::FetchConstantType::kVertex || fetch.size != 3 * 2) {
XELOGE("Unsupported resolve vertex buffer format");
assert_always();
@@ -997,10 +993,10 @@ bool GetResolveInfo(const RegisterFile& regs, const Memory& memory,
GetScissor(regs, scissor, false);
int32_t scissor_right = int32_t(scissor.offset[0] + scissor.extent[0]);
int32_t scissor_bottom = int32_t(scissor.offset[1] + scissor.extent[1]);
x0 = xe::clamp(x0, int32_t(scissor.offset[0]), scissor_right);
y0 = xe::clamp(y0, int32_t(scissor.offset[1]), scissor_bottom);
x1 = xe::clamp(x1, int32_t(scissor.offset[0]), scissor_right);
y1 = xe::clamp(y1, int32_t(scissor.offset[1]), scissor_bottom);
x0 = std::clamp(x0, int32_t(scissor.offset[0]), scissor_right);
y0 = std::clamp(y0, int32_t(scissor.offset[1]), scissor_bottom);
x1 = std::clamp(x1, int32_t(scissor.offset[0]), scissor_right);
y1 = std::clamp(y1, int32_t(scissor.offset[1]), scissor_bottom);
assert_true(x0 <= x1 && y0 <= y1);
@@ -1114,7 +1110,7 @@ bool GetResolveInfo(const RegisterFile& regs, const Memory& memory,
}
// Calculate the destination memory extent.
uint32_t rb_copy_dest_base = regs[XE_GPU_REG_RB_COPY_DEST_BASE].u32;
uint32_t rb_copy_dest_base = regs[XE_GPU_REG_RB_COPY_DEST_BASE];
uint32_t copy_dest_base_adjusted = rb_copy_dest_base;
uint32_t copy_dest_extent_start, copy_dest_extent_end;
auto rb_copy_dest_pitch = regs.Get<reg::RB_COPY_DEST_PITCH>();
@@ -1284,9 +1280,10 @@ bool GetResolveInfo(const RegisterFile& regs, const Memory& memory,
info_out.copy_dest_info.copy_dest_swap = false;
}
info_out.rb_depth_clear = regs[XE_GPU_REG_RB_DEPTH_CLEAR].u32;
info_out.rb_color_clear = regs[XE_GPU_REG_RB_COLOR_CLEAR].u32;
info_out.rb_color_clear_lo = regs[XE_GPU_REG_RB_COLOR_CLEAR_LO].u32;
info_out.rb_depth_clear = regs[XE_GPU_REG_RB_DEPTH_CLEAR];
info_out.rb_color_clear = regs[XE_GPU_REG_RB_COLOR_CLEAR];
info_out.rb_color_clear_lo = regs[XE_GPU_REG_RB_COLOR_CLEAR_LO];
#if 0
XELOGD(
"Resolve: {},{} <= x,y < {},{}, {} -> {} at 0x{:08X} (potentially "
@@ -1377,4 +1374,4 @@ ResolveCopyShaderIndex ResolveInfo::GetCopyShader(
} // namespace draw_util
} // namespace gpu
} // namespace xe
} // namespace xe
+7 -7
View File
@@ -373,12 +373,12 @@ struct GetViewportInfoArgs {
pa_cl_vte_cntl = regs.Get<reg::PA_CL_VTE_CNTL>();
pa_su_sc_mode_cntl = regs.Get<reg::PA_SU_SC_MODE_CNTL>();
pa_su_vtx_cntl = regs.Get<reg::PA_SU_VTX_CNTL>();
PA_CL_VPORT_XSCALE = regs[XE_GPU_REG_PA_CL_VPORT_XSCALE].f32;
PA_CL_VPORT_YSCALE = regs[XE_GPU_REG_PA_CL_VPORT_YSCALE].f32;
PA_CL_VPORT_ZSCALE = regs[XE_GPU_REG_PA_CL_VPORT_ZSCALE].f32;
PA_CL_VPORT_XOFFSET = regs[XE_GPU_REG_PA_CL_VPORT_XOFFSET].f32;
PA_CL_VPORT_YOFFSET = regs[XE_GPU_REG_PA_CL_VPORT_YOFFSET].f32;
PA_CL_VPORT_ZOFFSET = regs[XE_GPU_REG_PA_CL_VPORT_ZOFFSET].f32;
PA_CL_VPORT_XSCALE = regs.Get<float>(XE_GPU_REG_PA_CL_VPORT_XSCALE);
PA_CL_VPORT_YSCALE = regs.Get<float>(XE_GPU_REG_PA_CL_VPORT_YSCALE);
PA_CL_VPORT_ZSCALE = regs.Get<float>(XE_GPU_REG_PA_CL_VPORT_ZSCALE);
PA_CL_VPORT_XOFFSET = regs.Get<float>(XE_GPU_REG_PA_CL_VPORT_XOFFSET);
PA_CL_VPORT_YOFFSET = regs.Get<float>(XE_GPU_REG_PA_CL_VPORT_YOFFSET);
PA_CL_VPORT_ZOFFSET = regs.Get<float>(XE_GPU_REG_PA_CL_VPORT_ZOFFSET);
pa_sc_window_offset = regs.Get<reg::PA_SC_WINDOW_OFFSET>();
depth_format = regs.Get<reg::RB_DEPTH_INFO>().depth_format;
}
@@ -767,4 +767,4 @@ bool GetResolveInfo(const RegisterFile& regs, const Memory& memory,
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_DRAW_UTIL_H_
#endif // XENIA_GPU_DRAW_UTIL_H_
+7 -8
View File
@@ -17,6 +17,7 @@
#include "xenia/base/assert.h"
#include "xenia/base/math.h"
#include "xenia/base/memory.h"
namespace xe {
namespace gpu {
@@ -1103,10 +1104,10 @@ struct Src : OperandAddress {
}
static Src LI(int32_t x) { return LI(x, x, x, x); }
static Src LF(float x, float y, float z, float w) {
return LU(*reinterpret_cast<const uint32_t*>(&x),
*reinterpret_cast<const uint32_t*>(&y),
*reinterpret_cast<const uint32_t*>(&z),
*reinterpret_cast<const uint32_t*>(&w));
return LU(xe::memory::Reinterpret<uint32_t>(x),
xe::memory::Reinterpret<uint32_t>(y),
xe::memory::Reinterpret<uint32_t>(z),
xe::memory::Reinterpret<uint32_t>(w));
}
static Src LF(float x) { return LF(x, x, x, x); }
static Src LP(const uint32_t* xyzw) {
@@ -1223,12 +1224,10 @@ struct Src : OperandAddress {
bool negate) {
if (is_integer) {
if (absolute) {
*reinterpret_cast<int32_t*>(&value) =
std::abs(*reinterpret_cast<const int32_t*>(&value));
value = uint32_t(std::abs(int32_t(value)));
}
if (negate) {
*reinterpret_cast<int32_t*>(&value) =
-*reinterpret_cast<const int32_t*>(&value);
value = uint32_t(-int32_t(value));
}
} else {
if (absolute) {
+3 -3
View File
@@ -258,7 +258,7 @@ uint32_t GraphicsSystem::ReadRegister(uint32_t addr) {
}
assert_true(r < RegisterFile::kRegisterCount);
return register_file()->values[r].u32;
return register_file()->values[r];
}
void GraphicsSystem::WriteRegister(uint32_t addr, uint32_t value) {
@@ -276,7 +276,7 @@ void GraphicsSystem::WriteRegister(uint32_t addr, uint32_t value) {
}
assert_true(r < RegisterFile::kRegisterCount);
this->register_file()->values[r].u32 = value;
this->register_file()->values[r] = value;
}
void GraphicsSystem::InitializeRingBuffer(uint32_t ptr, uint32_t size_log2) {
@@ -379,4 +379,4 @@ bool GraphicsSystem::Restore(ByteStream* stream) {
}
} // namespace gpu
} // namespace xe
} // namespace xe
+2 -2
View File
@@ -67,7 +67,7 @@ struct PacketAction {
union {
struct {
uint32_t index;
RegisterFile::RegisterValue value;
uint32_t value;
} register_write;
struct {
uint64_t value;
@@ -194,7 +194,7 @@ struct PacketAction {
PacketAction action;
action.type = Type::kRegisterWrite;
action.register_write.index = index;
action.register_write.value.u32 = value;
action.register_write.value = value;
return action;
}
+32 -30
View File
@@ -706,23 +706,27 @@ bool COMMAND_PROCESSOR::ExecutePacketType3_WAIT_REG_MEM(
uint32_t ref = reader_.ReadAndSwap<uint32_t>();
uint32_t mask = reader_.ReadAndSwap<uint32_t>();
uint32_t wait = reader_.ReadAndSwap<uint32_t>();
bool is_memory = (wait_info & 0x10) != 0;
assert_true(is_memory || poll_reg_addr < RegisterFile::kRegisterCount);
const volatile uint32_t& value_ref =
is_memory ? *reinterpret_cast<uint32_t*>(memory_->TranslatePhysical(
poll_reg_addr & ~uint32_t(0x3)))
: register_file_->values[poll_reg_addr];
bool matched = false;
do {
uint32_t value;
if (wait_info & 0x10) {
// Memory.
auto endianness = static_cast<xenos::Endian>(poll_reg_addr & 0x3);
poll_reg_addr &= ~0x3;
value = xe::load<uint32_t>(memory_->TranslatePhysical(poll_reg_addr));
value = GpuSwap(value, endianness);
trace_writer_.WriteMemoryRead(CpuToGpu(poll_reg_addr), 4);
uint32_t value = value_ref;
if (is_memory) {
trace_writer_.WriteMemoryRead(CpuToGpu(poll_reg_addr & ~uint32_t(0x3)),
sizeof(uint32_t));
value = xenos::GpuSwap(value,
static_cast<xenos::Endian>(poll_reg_addr & 0x3));
} else {
// Register.
assert_true(poll_reg_addr < RegisterFile::kRegisterCount);
value = register_file_->values[poll_reg_addr].u32;
if (poll_reg_addr == XE_GPU_REG_COHER_STATUS_HOST) {
MakeCoherent();
value = register_file_->values[poll_reg_addr].u32;
value = value_ref;
}
}
matched = MatchValueAndRef(value & mask, ref, wait_info);
@@ -758,17 +762,17 @@ bool COMMAND_PROCESSOR::ExecutePacketType3_REG_RMW(uint32_t packet,
uint32_t rmw_info = reader_.ReadAndSwap<uint32_t>();
uint32_t and_mask = reader_.ReadAndSwap<uint32_t>();
uint32_t or_mask = reader_.ReadAndSwap<uint32_t>();
uint32_t value = register_file_->values[rmw_info & 0x1FFF].u32;
uint32_t value = register_file_->values[rmw_info & 0x1FFF];
if ((rmw_info >> 31) & 0x1) {
// & reg
value &= register_file_->values[and_mask & 0x1FFF].u32;
value &= register_file_->values[and_mask & 0x1FFF];
} else {
// & imm
value &= and_mask;
}
if ((rmw_info >> 30) & 0x1) {
// | reg
value |= register_file_->values[or_mask & 0x1FFF].u32;
value |= register_file_->values[or_mask & 0x1FFF];
} else {
// | imm
value |= or_mask;
@@ -788,7 +792,7 @@ bool COMMAND_PROCESSOR::ExecutePacketType3_REG_TO_MEM(
uint32_t reg_val;
assert_true(reg_addr < RegisterFile::kRegisterCount);
reg_val = register_file_->values[reg_addr].u32;
reg_val = register_file_->values[reg_addr];
auto endianness = static_cast<xenos::Endian>(mem_addr & 0x3);
mem_addr &= ~0x3;
@@ -836,7 +840,7 @@ bool COMMAND_PROCESSOR::ExecutePacketType3_COND_WRITE(
} else {
// Register.
assert_true(poll_reg_addr < RegisterFile::kRegisterCount);
value = register_file_->values[poll_reg_addr].u32;
value = register_file_->values[poll_reg_addr];
}
bool matched = MatchValueAndRef(value & mask, ref, wait_info);
@@ -858,7 +862,7 @@ bool COMMAND_PROCESSOR::ExecutePacketType3_COND_WRITE(
}
XE_FORCEINLINE
void COMMAND_PROCESSOR::WriteEventInitiator(uint32_t value) XE_RESTRICT {
register_file_->values[XE_GPU_REG_VGT_EVENT_INITIATOR].u32 = value;
register_file_->values[XE_GPU_REG_VGT_EVENT_INITIATOR] = value;
}
bool COMMAND_PROCESSOR::ExecutePacketType3_EVENT_WRITE(
uint32_t packet, uint32_t count) XE_RESTRICT {
@@ -898,10 +902,8 @@ bool COMMAND_PROCESSOR::ExecutePacketType3_EVENT_WRITE_SHD(
data_value = GpuSwap(data_value, endianness);
uint8_t* write_destination = memory_->TranslatePhysical(address);
if (address > 0x1FFFFFFF) {
uint32_t writeback_base =
register_file_->values[XE_GPU_REG_WRITEBACK_BASE].u32;
uint32_t writeback_size =
register_file_->values[XE_GPU_REG_WRITEBACK_SIZE].u32;
uint32_t writeback_base = register_file_->values[XE_GPU_REG_WRITEBACK_BASE];
uint32_t writeback_size = register_file_->values[XE_GPU_REG_WRITEBACK_SIZE];
uint32_t writeback_offset = address - writeback_base;
// check whether the guest has written writeback base. if they haven't, skip
// the offset check
@@ -967,7 +969,7 @@ bool COMMAND_PROCESSOR::ExecutePacketType3_EVENT_WRITE_ZPD(
if (fake_sample_count >= 0) {
auto* pSampleCounts =
memory_->TranslatePhysical<xe_gpu_depth_sample_counts*>(
register_file_->values[XE_GPU_REG_RB_SAMPLE_COUNT_ADDR].u32);
register_file_->values[XE_GPU_REG_RB_SAMPLE_COUNT_ADDR]);
// 0xFFFFFEED is written to this two locations by D3D only on D3DISSUE_END
// and used to detect a finished query.
bool is_end_via_z_pass = pSampleCounts->ZPass_A == kQueryFinished &&
@@ -1003,7 +1005,7 @@ bool COMMAND_PROCESSOR::ExecutePacketType3Draw(
vgt_draw_initiator.value = reader_.ReadAndSwap<uint32_t>();
--count_remaining;
register_file_->values[XE_GPU_REG_VGT_DRAW_INITIATOR].u32 =
register_file_->values[XE_GPU_REG_VGT_DRAW_INITIATOR] =
vgt_draw_initiator.value;
bool draw_succeeded = true;
// TODO(Triang3l): Remove IndexBufferInfo and replace handling of all this
@@ -1025,7 +1027,7 @@ bool COMMAND_PROCESSOR::ExecutePacketType3Draw(
}
uint32_t vgt_dma_base = reader_.ReadAndSwap<uint32_t>();
--count_remaining;
register_file_->values[XE_GPU_REG_VGT_DMA_BASE].u32 = vgt_dma_base;
register_file_->values[XE_GPU_REG_VGT_DMA_BASE] = vgt_dma_base;
reg::VGT_DMA_SIZE vgt_dma_size;
assert_not_zero(count_remaining);
if (!count_remaining) {
@@ -1034,7 +1036,7 @@ bool COMMAND_PROCESSOR::ExecutePacketType3Draw(
}
vgt_dma_size.value = reader_.ReadAndSwap<uint32_t>();
--count_remaining;
register_file_->values[XE_GPU_REG_VGT_DMA_SIZE].u32 = vgt_dma_size.value;
register_file_->values[XE_GPU_REG_VGT_DMA_SIZE] = vgt_dma_size.value;
uint32_t index_size_bytes =
vgt_draw_initiator.index_size == xenos::IndexFormat::kInt16
@@ -1341,10 +1343,10 @@ bool COMMAND_PROCESSOR::ExecutePacketType3_VIZ_QUERY(
// The scan converter writes the internal result back to the register here.
// We just fake it and say it was visible in case it is read back.
if (id < 32) {
register_file_->values[XE_GPU_REG_PA_SC_VIZ_QUERY_STATUS_0].u32 |=
uint32_t(1) << id;
register_file_->values[XE_GPU_REG_PA_SC_VIZ_QUERY_STATUS_0] |= uint32_t(1)
<< id;
} else {
register_file_->values[XE_GPU_REG_PA_SC_VIZ_QUERY_STATUS_1].u32 |=
register_file_->values[XE_GPU_REG_PA_SC_VIZ_QUERY_STATUS_1] |=
uint32_t(1) << (id - 32);
}
}
@@ -1423,4 +1425,4 @@ void COMMAND_PROCESSOR::ExecutePacket(uint32_t ptr, uint32_t count) {
}
} while (reader_.read_count());
reader_ = old_reader;
}
}
+4 -4
View File
@@ -498,8 +498,8 @@ bool PrimitiveProcessor::Process(ProcessingResult& result_out) {
uint32_t index_size_log2 =
guest_index_format == xenos::IndexFormat::kInt16 ? 1 : 2;
// The base should already be aligned, but aligning here too for safety.
guest_index_base = regs[XE_GPU_REG_VGT_DMA_BASE].u32 &
~uint32_t((1 << index_size_log2) - 1);
guest_index_base =
regs[XE_GPU_REG_VGT_DMA_BASE] & ~uint32_t((1 << index_size_log2) - 1);
guest_index_buffer_needed_bytes = guest_draw_vertex_count
<< index_size_log2;
if (guest_index_base > SharedMemory::kBufferSize ||
@@ -652,8 +652,8 @@ bool PrimitiveProcessor::Process(ProcessingResult& result_out) {
uint32_t index_size_log2 =
guest_index_format == xenos::IndexFormat::kInt16 ? 1 : 2;
// The base should already be aligned, but aligning here too for safety.
guest_index_base = regs[XE_GPU_REG_VGT_DMA_BASE].u32 &
~uint32_t((1 << index_size_log2) - 1);
guest_index_base =
regs[XE_GPU_REG_VGT_DMA_BASE] & ~uint32_t((1 << index_size_log2) - 1);
guest_index_buffer_needed_bytes = guest_draw_vertex_count
<< index_size_log2;
if (guest_index_base > SharedMemory::kBufferSize ||
+42 -24
View File
@@ -12,8 +12,12 @@
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include "xenia/base/assert.h"
#include "xenia/base/memory.h"
#include "xenia/gpu/registers.h"
#include "xenia/gpu/xenos.h"
namespace xe {
namespace gpu {
@@ -34,39 +38,53 @@ class RegisterFile {
static const RegisterInfo* GetRegisterInfo(uint32_t index);
static bool IsValidRegister(uint32_t index);
static constexpr size_t kRegisterCount = 0x5003;
union RegisterValue {
uint32_t u32;
float f32;
};
RegisterValue values[kRegisterCount];
uint32_t values[kRegisterCount];
const uint32_t& operator[](uint32_t reg) const { return values[reg]; }
uint32_t& operator[](uint32_t reg) { return values[reg]; }
const RegisterValue& operator[](uint32_t reg) const { return values[reg]; }
RegisterValue& operator[](uint32_t reg) { return values[reg]; }
const RegisterValue& operator[](Register reg) const { return values[reg]; }
RegisterValue& operator[](Register reg) { return values[reg]; }
template <typename T>
const T& Get(uint32_t reg) const {
return *reinterpret_cast<const T*>(&values[reg]);
T Get(uint32_t reg) const {
return xe::memory::Reinterpret<T>(values[reg]);
}
template <typename T>
T& Get(uint32_t reg) {
return *reinterpret_cast<T*>(&values[reg]);
T Get(Register reg) const {
return Get<T>(static_cast<uint32_t>(reg));
}
template <typename T>
const T& Get(Register reg) const {
return *reinterpret_cast<const T*>(&values[reg]);
T Get() const {
return Get<T>(T::register_index);
}
template <typename T>
T& Get(Register reg) {
return *reinterpret_cast<T*>(&values[reg]);
xenos::xe_gpu_vertex_fetch_t GetVertexFetch(uint32_t index) const {
assert_true(index < 96);
xenos::xe_gpu_vertex_fetch_t fetch;
std::memcpy(&fetch,
&values[XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0 +
(sizeof(fetch) / sizeof(uint32_t)) * index],
sizeof(fetch));
return fetch;
}
template <typename T>
const T& Get() const {
return *reinterpret_cast<const T*>(&values[T::register_index]);
xenos::xe_gpu_texture_fetch_t GetTextureFetch(uint32_t index) const {
assert_true(index < 32);
xenos::xe_gpu_texture_fetch_t fetch;
std::memcpy(&fetch,
&values[XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0 +
(sizeof(fetch) / sizeof(uint32_t)) * index],
sizeof(fetch));
return fetch;
}
template <typename T>
T& Get() {
return *reinterpret_cast<T*>(&values[T::register_index]);
xenos::xe_gpu_memexport_stream_t GetMemExportStream(
uint32_t float_constant_index) const {
assert_true(float_constant_index < 512);
xenos::xe_gpu_memexport_stream_t stream;
std::memcpy(
&stream,
&values[XE_GPU_REG_SHADER_CONSTANT_000_X + 4 * float_constant_index],
sizeof(stream));
return stream;
}
};
+115 -100
View File
@@ -21,10 +21,7 @@ void ShaderInterpreter::Execute() {
state_.Reset();
const uint32_t* bool_constants =
&register_file_[XE_GPU_REG_SHADER_CONSTANT_BOOL_000_031].u32;
const xenos::LoopConstant* loop_constants =
reinterpret_cast<const xenos::LoopConstant*>(
&register_file_[XE_GPU_REG_SHADER_CONSTANT_LOOP_00].u32);
&register_file_[XE_GPU_REG_SHADER_CONSTANT_BOOL_000_031];
bool exec_ended = false;
uint32_t cf_index_next = 1;
@@ -133,8 +130,8 @@ void ShaderInterpreter::Execute() {
cf_index_next = cf_loop_start.address();
continue;
}
xenos::LoopConstant loop_constant =
loop_constants[cf_loop_start.loop_id()];
auto loop_constant = register_file_.Get<xenos::LoopConstant>(
XE_GPU_REG_SHADER_CONSTANT_LOOP_00 + 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];
@@ -163,8 +160,11 @@ void ShaderInterpreter::Execute() {
&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);
assert_zero(
std::memcmp(&loop_constant,
&register_file_[XE_GPU_REG_SHADER_CONSTANT_LOOP_00 +
cf_loop_end.loop_id()],
sizeof(loop_constant)));
uint32_t loop_iterator =
++state_.loop_iterators[state_.loop_stack_depth - 1];
if (loop_iterator < loop_constant.count &&
@@ -250,28 +250,31 @@ void ShaderInterpreter::Execute() {
}
}
const float* ShaderInterpreter::GetFloatConstant(
const std::array<float, 4> 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;
return std::array<float, 4>();
}
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;
return std::array<float, 4>();
}
index += base_and_size_minus_1.base;
if (index >= 512) {
return zero;
return std::array<float, 4>();
}
return &register_file_[XE_GPU_REG_SHADER_CONSTANT_000_X + 4 * index].f32;
std::array<float, 4> value;
std::memcpy(value.data(),
&register_file_[XE_GPU_REG_SHADER_CONSTANT_000_X + 4 * index],
sizeof(float) * 4);
return value;
}
void ShaderInterpreter::ExecuteAluInstruction(ucode::AluInstruction instr) {
@@ -290,6 +293,7 @@ void ShaderInterpreter::ExecuteAluInstruction(ucode::AluInstruction instr) {
const float* vector_src_ptr;
uint32_t vector_src_register = instr.src_reg(1 + i);
bool vector_src_absolute = false;
std::array<float, 4> vector_src_float_constant;
if (instr.src_is_temp(1 + i)) {
vector_src_ptr = GetTempRegister(
ucode::AluInstruction::src_temp_reg(vector_src_register),
@@ -297,9 +301,10 @@ void ShaderInterpreter::ExecuteAluInstruction(ucode::AluInstruction instr) {
vector_src_absolute = ucode::AluInstruction::is_src_temp_value_absolute(
vector_src_register);
} else {
vector_src_ptr = GetFloatConstant(
vector_src_float_constant = GetFloatConstant(
vector_src_register, instr.src_const_is_addressed(1 + i),
instr.is_const_address_register_relative());
vector_src_ptr = vector_src_float_constant.data();
}
uint32_t vector_src_absolute_mask =
~(uint32_t(vector_src_absolute) << 31);
@@ -334,16 +339,18 @@ void ShaderInterpreter::ExecuteAluInstruction(ucode::AluInstruction instr) {
} 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];
vector_result[i] =
std::isgreaterequal(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];
vector_result[i] =
std::isless(vector_operands[0][i], vector_operands[1][i])
? vector_operands[0][i]
: vector_operands[1][i];
}
} break;
case ucode::AluVectorOpcode::kSeq: {
@@ -354,14 +361,14 @@ void ShaderInterpreter::ExecuteAluInstruction(ucode::AluInstruction instr) {
} 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]);
vector_result[i] = float(
std::isgreater(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]);
vector_result[i] = float(std::isgreaterequal(vector_operands[0][i],
vector_operands[1][i]));
}
} break;
case ucode::AluVectorOpcode::kSne: {
@@ -407,14 +414,14 @@ void ShaderInterpreter::ExecuteAluInstruction(ucode::AluInstruction instr) {
} break;
case ucode::AluVectorOpcode::kCndGe: {
for (uint32_t i = 0; i < 4; ++i) {
vector_result[i] = vector_operands[0][i] >= 0.0f
vector_result[i] = std::isgreaterequal(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_result[i] = std::isgreater(vector_operands[0][i], 0.0f)
? vector_operands[1][i]
: vector_operands[2][i];
}
@@ -466,32 +473,38 @@ void ShaderInterpreter::ExecuteAluInstruction(ucode::AluInstruction instr) {
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) {
bool z_negative = std::isless(z, 0.0f);
vector_result[0] = -y;
vector_result[1] = z < 0.0f ? -x : x;
vector_result[1] = z_negative ? -x : x;
vector_result[2] = z;
vector_result[3] = z < 0.0f ? 5.0f : 4.0f;
vector_result[3] = z_negative ? 5.0f : 4.0f;
} else if (y_abs >= x_abs) {
vector_result[0] = y < 0.0f ? -z : z;
bool y_negative = std::isless(y, 0.0f);
vector_result[0] = y_negative ? -z : z;
vector_result[1] = x;
vector_result[2] = y;
vector_result[3] = y < 0.0f ? 3.0f : 2.0f;
vector_result[3] = y_negative ? 3.0f : 2.0f;
} else {
bool x_negative = std::isless(x, 0.0f);
vector_result[0] = -y;
vector_result[1] = x < 0.0f ? z : -z;
vector_result[1] = x_negative ? z : -z;
vector_result[2] = x;
vector_result[3] = x < 0.0f ? 1.0f : 0.0f;
vector_result[3] = x_negative ? 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]) {
if (std::isgreaterequal(vector_operands[0][0], vector_operands[0][1]) &&
std::isgreaterequal(vector_operands[0][0], vector_operands[0][2]) &&
std::isgreaterequal(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]) {
} else if (std::isgreaterequal(vector_operands[0][1],
vector_operands[0][2]) &&
std::isgreaterequal(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]) {
} else if (std::isgreaterequal(vector_operands[0][2],
vector_operands[0][3])) {
vector_result[0] = vector_operands[0][2];
} else {
vector_result[0] = vector_operands[0][3];
@@ -517,21 +530,21 @@ void ShaderInterpreter::ExecuteAluInstruction(ucode::AluInstruction instr) {
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;
state_.predicate = vector_operands[0][3] == 0.0f &&
std::isgreater(vector_operands[1][3], 0.0f);
vector_result[0] = (vector_operands[0][0] == 0.0f &&
std::isgreater(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;
state_.predicate = vector_operands[0][3] == 0.0f &&
std::isgreaterequal(vector_operands[1][3], 0.0f);
vector_result[0] = (vector_operands[0][0] == 0.0f &&
std::isgreaterequal(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
@@ -545,19 +558,19 @@ void ShaderInterpreter::ExecuteAluInstruction(ucode::AluInstruction instr) {
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]);
vector_result[0] = float(
std::isgreater(vector_operands[0][0], vector_operands[1][0]) ||
std::isgreater(vector_operands[0][1], vector_operands[1][1]) ||
std::isgreater(vector_operands[0][2], vector_operands[1][2]) ||
std::isgreater(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]);
vector_result[0] = float(
std::isgreaterequal(vector_operands[0][0], vector_operands[1][0]) ||
std::isgreaterequal(vector_operands[0][1], vector_operands[1][1]) ||
std::isgreaterequal(vector_operands[0][2], vector_operands[1][2]) ||
std::isgreaterequal(vector_operands[0][3], vector_operands[1][3]));
replicate_vector_result_x = true;
} break;
case ucode::AluVectorOpcode::kKillNe: {
@@ -578,14 +591,13 @@ void ShaderInterpreter::ExecuteAluInstruction(ucode::AluInstruction instr) {
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));
xe::clamp_float(vector_operands[0][3], -256.0f, 255.0f) + 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];
vector_result[i] =
std::isgreaterequal(vector_operands[0][i], vector_operands[1][i])
? vector_operands[0][i]
: vector_operands[1][i];
}
} break;
default: {
@@ -611,6 +623,7 @@ void ShaderInterpreter::ExecuteAluInstruction(ucode::AluInstruction instr) {
// r#/c#.w or r#/c#.wx.
const float* scalar_src_ptr;
uint32_t scalar_src_register = instr.src_reg(3);
std::array<float, 4> scalar_src_float_constant;
if (instr.src_is_temp(3)) {
scalar_src_ptr = GetTempRegister(
ucode::AluInstruction::src_temp_reg(scalar_src_register),
@@ -618,9 +631,10 @@ void ShaderInterpreter::ExecuteAluInstruction(ucode::AluInstruction instr) {
scalar_src_absolute = ucode::AluInstruction::is_src_temp_value_absolute(
scalar_src_register);
} else {
scalar_src_ptr = GetFloatConstant(
scalar_src_float_constant = GetFloatConstant(
scalar_src_register, instr.src_const_is_addressed(3),
instr.is_const_address_register_relative());
scalar_src_ptr = scalar_src_float_constant.data();
}
uint32_t scalar_src_swizzle = instr.src_swizzle(3);
scalar_operand_component_count =
@@ -688,7 +702,8 @@ void ShaderInterpreter::ExecuteAluInstruction(ucode::AluInstruction instr) {
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) {
!std::isfinite(scalar_operands[1]) ||
std::islessequal(scalar_operands[1], 0.0f)) {
state_.previous_scalar = -FLT_MAX;
} else {
// Direct3D 9 behavior (0 or denormal * anything = +0).
@@ -699,23 +714,26 @@ void ShaderInterpreter::ExecuteAluInstruction(ucode::AluInstruction instr) {
}
} break;
case ucode::AluScalarOpcode::kMaxs: {
state_.previous_scalar = scalar_operands[0] >= scalar_operands[1]
? scalar_operands[0]
: scalar_operands[1];
state_.previous_scalar =
std::isgreaterequal(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];
state_.previous_scalar =
std::isless(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);
state_.previous_scalar = float(std::isgreater(scalar_operands[0], 0.0f));
} break;
case ucode::AluScalarOpcode::kSges: {
state_.previous_scalar = float(scalar_operands[0] >= 0.0f);
state_.previous_scalar =
float(std::isgreaterequal(scalar_operands[0], 0.0f));
} break;
case ucode::AluScalarOpcode::kSnes: {
state_.previous_scalar = float(scalar_operands[0] != 0.0f);
@@ -781,22 +799,20 @@ void ShaderInterpreter::ExecuteAluInstruction(ucode::AluInstruction instr) {
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];
xe::clamp_float(scalar_operands[0], -256.0f, 255.0f) + 0.5f));
state_.previous_scalar =
std::isgreaterequal(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];
std::floor(xe::clamp_float(scalar_operands[0], -256.0f, 255.0f)));
state_.previous_scalar =
std::isgreaterequal(scalar_operands[0], scalar_operands[1])
? scalar_operands[0]
: scalar_operands[1];
} break;
case ucode::AluScalarOpcode::kSubs:
case ucode::AluScalarOpcode::kSubsc0:
@@ -815,11 +831,11 @@ void ShaderInterpreter::ExecuteAluInstruction(ucode::AluInstruction instr) {
state_.previous_scalar = float(!state_.predicate);
} break;
case ucode::AluScalarOpcode::kSetpGt: {
state_.predicate = scalar_operands[0] > 0.0f;
state_.predicate = std::isgreater(scalar_operands[0], 0.0f);
state_.previous_scalar = float(!state_.predicate);
} break;
case ucode::AluScalarOpcode::kSetpGe: {
state_.predicate = scalar_operands[0] >= 0.0f;
state_.predicate = std::isgreaterequal(scalar_operands[0], 0.0f);
state_.previous_scalar = float(!state_.predicate);
} break;
case ucode::AluScalarOpcode::kSetpInv: {
@@ -831,7 +847,7 @@ void ShaderInterpreter::ExecuteAluInstruction(ucode::AluInstruction instr) {
} break;
case ucode::AluScalarOpcode::kSetpPop: {
float new_counter = scalar_operands[0] - 1.0f;
state_.predicate = new_counter <= 0.0f;
state_.predicate = std::islessequal(new_counter, 0.0f);
state_.previous_scalar = state_.predicate ? 0.0f : new_counter;
} break;
case ucode::AluScalarOpcode::kSetpClr: {
@@ -848,10 +864,11 @@ void ShaderInterpreter::ExecuteAluInstruction(ucode::AluInstruction instr) {
state_.previous_scalar = float(scalar_operands[0] == 0.0f);
} break;
case ucode::AluScalarOpcode::kKillsGt: {
state_.previous_scalar = float(scalar_operands[0] > 0.0f);
state_.previous_scalar = float(std::isgreater(scalar_operands[0], 0.0f));
} break;
case ucode::AluScalarOpcode::kKillsGe: {
state_.previous_scalar = float(scalar_operands[0] >= 0.0f);
state_.previous_scalar =
float(std::isgreaterequal(scalar_operands[0], 0.0f));
} break;
case ucode::AluScalarOpcode::kKillsNe: {
state_.previous_scalar = float(scalar_operands[0] != 0.0f);
@@ -877,11 +894,11 @@ void ShaderInterpreter::ExecuteAluInstruction(ucode::AluInstruction instr) {
if (instr.vector_clamp()) {
for (uint32_t i = 0; i < 4; ++i) {
vector_result[i] = xe::saturate_unsigned(vector_result[i]);
vector_result[i] = xe::saturate(vector_result[i]);
}
}
float scalar_result = instr.scalar_clamp()
? xe::saturate_unsigned(state_.previous_scalar)
? xe::saturate(state_.previous_scalar)
: state_.previous_scalar;
uint32_t scalar_result_write_mask = instr.GetScalarOpResultWriteMask();
@@ -977,10 +994,8 @@ void ShaderInterpreter::ExecuteVertexFetchInstruction(
state_.vfetch_full_last = instr;
}
xenos::xe_gpu_vertex_fetch_t fetch_constant =
*reinterpret_cast<const xenos::xe_gpu_vertex_fetch_t*>(
&register_file_[XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0 +
state_.vfetch_full_last.fetch_constant_index()]);
xenos::xe_gpu_vertex_fetch_t fetch_constant = register_file_.GetVertexFetch(
state_.vfetch_full_last.fetch_constant_index());
if (!instr.is_mini_fetch()) {
// Get the part of the address that depends on vfetch_full data.
+3 -2
View File
@@ -11,6 +11,7 @@
#define XENIA_GPU_SHADER_INTERPRETER_H_
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
@@ -117,8 +118,8 @@ class ShaderInterpreter {
float* GetTempRegister(uint32_t address, bool is_relative) {
return temp_registers_[GetTempRegisterIndex(address, is_relative)];
}
const float* GetFloatConstant(uint32_t address, bool is_relative,
bool relative_address_is_a0) const;
const std::array<float, 4> GetFloatConstant(
uint32_t address, bool is_relative, bool relative_address_is_a0) const;
void ExecuteAluInstruction(ucode::AluInstruction instr);
void StoreFetchResult(uint32_t dest, bool is_dest_relative, uint32_t swizzle,
+192
View File
@@ -13,6 +13,8 @@
#include <utility>
#include <vector>
#include "xenia/base/assert.h"
namespace xe {
namespace gpu {
@@ -101,5 +103,195 @@ spv::Id SpirvBuilder::createTriBuiltinCall(spv::Id result_type,
return result;
}
SpirvBuilder::IfBuilder::IfBuilder(spv::Id condition, unsigned int control,
SpirvBuilder& builder,
unsigned int thenWeight,
unsigned int elseWeight)
: builder(builder),
condition(condition),
control(control),
thenWeight(thenWeight),
elseWeight(elseWeight),
function(builder.getBuildPoint()->getParent()) {
// Make the blocks, but only put the then-block into the function, the
// else-block and merge-block will be added later, in order, after earlier
// code is emitted.
thenBlock = new spv::Block(builder.getUniqueId(), function);
elseBlock = nullptr;
mergeBlock = new spv::Block(builder.getUniqueId(), function);
// Save the current block, so that we can add in the flow control split when
// makeEndIf is called.
headerBlock = builder.getBuildPoint();
spv::Id headerBlockId = headerBlock->getId();
thenPhiParent = headerBlockId;
elsePhiParent = headerBlockId;
function.addBlock(thenBlock);
builder.setBuildPoint(thenBlock);
}
void SpirvBuilder::IfBuilder::makeBeginElse(bool branchToMerge) {
#ifndef NDEBUG
assert_true(currentBranch == Branch::kThen);
#endif
if (branchToMerge) {
// Close out the "then" by having it jump to the mergeBlock.
thenPhiParent = builder.getBuildPoint()->getId();
builder.createBranch(mergeBlock);
}
// Make the first else block and add it to the function.
elseBlock = new spv::Block(builder.getUniqueId(), function);
function.addBlock(elseBlock);
// Start building the else block.
builder.setBuildPoint(elseBlock);
#ifndef NDEBUG
currentBranch = Branch::kElse;
#endif
}
void SpirvBuilder::IfBuilder::makeEndIf(bool branchToMerge) {
#ifndef NDEBUG
assert_true(currentBranch == Branch::kThen || currentBranch == Branch::kElse);
#endif
if (branchToMerge) {
// Jump to the merge block.
(elseBlock ? elsePhiParent : thenPhiParent) =
builder.getBuildPoint()->getId();
builder.createBranch(mergeBlock);
}
// Go back to the headerBlock and make the flow control split.
builder.setBuildPoint(headerBlock);
builder.createSelectionMerge(mergeBlock, control);
{
spv::Block* falseBlock = elseBlock ? elseBlock : mergeBlock;
std::unique_ptr<spv::Instruction> branch =
std::make_unique<spv::Instruction>(spv::OpBranchConditional);
branch->addIdOperand(condition);
branch->addIdOperand(thenBlock->getId());
branch->addIdOperand(falseBlock->getId());
if (thenWeight || elseWeight) {
branch->addImmediateOperand(thenWeight);
branch->addImmediateOperand(elseWeight);
}
builder.getBuildPoint()->addInstruction(std::move(branch));
thenBlock->addPredecessor(builder.getBuildPoint());
falseBlock->addPredecessor(builder.getBuildPoint());
}
// Add the merge block to the function.
function.addBlock(mergeBlock);
builder.setBuildPoint(mergeBlock);
#ifndef NDEBUG
currentBranch = Branch::kMerge;
#endif
}
spv::Id SpirvBuilder::IfBuilder::createMergePhi(spv::Id then_variable,
spv::Id else_variable) const {
assert_true(builder.getBuildPoint() == mergeBlock);
return builder.createQuadOp(spv::OpPhi, builder.getTypeId(then_variable),
then_variable, getThenPhiParent(), else_variable,
getElsePhiParent());
}
SpirvBuilder::SwitchBuilder::SwitchBuilder(spv::Id selector,
unsigned int selection_control,
SpirvBuilder& builder)
: builder_(builder),
selector_(selector),
selection_control_(selection_control),
function_(builder.getBuildPoint()->getParent()),
header_block_(builder.getBuildPoint()),
default_phi_parent_(builder.getBuildPoint()->getId()) {
merge_block_ = new spv::Block(builder_.getUniqueId(), function_);
}
void SpirvBuilder::SwitchBuilder::makeBeginDefault() {
assert_null(default_block_);
endSegment();
default_block_ = new spv::Block(builder_.getUniqueId(), function_);
function_.addBlock(default_block_);
default_block_->addPredecessor(header_block_);
builder_.setBuildPoint(default_block_);
current_branch_ = Branch::kDefault;
}
void SpirvBuilder::SwitchBuilder::makeBeginCase(unsigned int literal) {
endSegment();
auto case_block = new spv::Block(builder_.getUniqueId(), function_);
function_.addBlock(case_block);
cases_.emplace_back(literal, case_block->getId());
case_block->addPredecessor(header_block_);
builder_.setBuildPoint(case_block);
current_branch_ = Branch::kCase;
}
void SpirvBuilder::SwitchBuilder::addCurrentCaseLiteral(unsigned int literal) {
assert_true(current_branch_ == Branch::kCase);
cases_.emplace_back(literal, cases_.back().second);
}
void SpirvBuilder::SwitchBuilder::makeEndSwitch() {
endSegment();
builder_.setBuildPoint(header_block_);
builder_.createSelectionMerge(merge_block_, selection_control_);
std::unique_ptr<spv::Instruction> switch_instruction =
std::make_unique<spv::Instruction>(spv::OpSwitch);
switch_instruction->addIdOperand(selector_);
if (default_block_) {
switch_instruction->addIdOperand(default_block_->getId());
} else {
switch_instruction->addIdOperand(merge_block_->getId());
merge_block_->addPredecessor(header_block_);
}
for (const std::pair<unsigned int, spv::Id>& case_pair : cases_) {
switch_instruction->addImmediateOperand(case_pair.first);
switch_instruction->addIdOperand(case_pair.second);
}
builder_.getBuildPoint()->addInstruction(std::move(switch_instruction));
function_.addBlock(merge_block_);
builder_.setBuildPoint(merge_block_);
current_branch_ = Branch::kMerge;
}
void SpirvBuilder::SwitchBuilder::endSegment() {
assert_true(current_branch_ == Branch::kSelection ||
current_branch_ == Branch::kDefault ||
current_branch_ == Branch::kCase);
if (current_branch_ == Branch::kSelection) {
return;
}
if (!builder_.getBuildPoint()->isTerminated()) {
builder_.createBranch(merge_block_);
if (current_branch_ == Branch::kDefault) {
default_phi_parent_ = builder_.getBuildPoint()->getId();
}
}
current_branch_ = Branch::kSelection;
}
} // namespace gpu
} // namespace xe

Some files were not shown because too many files have changed in this diff Show More