mirror of
https://github.com/izzy2lost/xenia-edge.git
synced 2026-07-06 00:20:26 -07:00
Merge branch 'master' of https://github.com/xenia-project/xenia into canary_experimental
This commit is contained in:
@@ -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
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -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 =
|
||||
®s[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 =
|
||||
®s[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,
|
||||
®s[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,
|
||||
®s[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,
|
||||
®s[XE_GPU_REG_SHADER_CONSTANT_BOOL_000_031].u32);
|
||||
bool_loop_constants, ®s[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, ®s[XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0].u32);
|
||||
fetch_constants, ®s[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
|
||||
@@ -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 ||
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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_
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 ||
|
||||
|
||||
@@ -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
@@ -21,10 +21,7 @@ void ShaderInterpreter::Execute() {
|
||||
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);
|
||||
®ister_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,
|
||||
®ister_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 ®ister_file_[XE_GPU_REG_SHADER_CONSTANT_000_X + 4 * index].f32;
|
||||
std::array<float, 4> value;
|
||||
std::memcpy(value.data(),
|
||||
®ister_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*>(
|
||||
®ister_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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user