From a3304d252fac72f951ec3ce8b56a010edae05f63 Mon Sep 17 00:00:00 2001 From: Triang3l Date: Sun, 12 May 2024 19:21:37 +0300 Subject: [PATCH] [Base/GPU] Cleanup float comparisons and NaN and -0 in clamping C++ relational operators are supposed to raise FE_INVALID if an argument is NaN, use std::isless/greater[equal] instead where they were easy to locate (though there are other places possibly, mostly min/max and clamp usage was checked). Also fixes a copy-paste error making the CPU shader interpreter execute MINs as MAXs instead. --- src/xenia/apu/xma_context.cc | 2 +- src/xenia/base/math.h | 28 ++--- src/xenia/debug/ui/debug_window.cc | 12 +- src/xenia/gpu/draw_util.cc | 25 ++-- src/xenia/gpu/shader_interpreter.cc | 170 +++++++++++++++------------- src/xenia/gpu/trace_viewer.cc | 2 +- src/xenia/gpu/xenos.cc | 4 +- src/xenia/ui/immediate_drawer.cc | 24 ++-- src/xenia/ui/window_android.cc | 20 ++-- 9 files changed, 143 insertions(+), 144 deletions(-) diff --git a/src/xenia/apu/xma_context.cc b/src/xenia/apu/xma_context.cc index e9047654a..27b07ba28 100644 --- a/src/xenia/apu/xma_context.cc +++ b/src/xenia/apu/xma_context.cc @@ -921,7 +921,7 @@ void XmaContext::ConvertFrame(const uint8_t** samples, bool is_two_channel, auto in = reinterpret_cast(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(scaled_sample); diff --git a/src/xenia/base/math.h b/src/xenia/base/math.h index 889cf03ed..55dce4b45 100644 --- a/src/xenia/base/math.h +++ b/src/xenia/base/math.h @@ -60,20 +60,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 -constexpr T saturate_unsigned(T value) { - return std::min(static_cast(1.0f), std::max(static_cast(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 -constexpr T saturate_signed(T value) { - return std::min(static_cast(1.0f), std::max(static_cast(-1.0f), value)); +T saturate(T value) { + return clamp_float(value, static_cast(0.0f), static_cast(1.0f)); } // Gets the next power of two value that is greater than or equal to the given @@ -330,12 +332,6 @@ inline uint64_t rotate_left(uint64_t v, uint8_t sh) { } #endif // XE_PLATFORM_WIN32 -template -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 diff --git a/src/xenia/debug/ui/debug_window.cc b/src/xenia/debug/ui/debug_window.cc index d56bcfd03..07d4404db 100644 --- a/src/xenia/debug/ui/debug_window.cc +++ b/src/xenia/debug/ui/debug_window.cc @@ -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); diff --git a/src/xenia/gpu/draw_util.cc b/src/xenia/gpu/draw_util.cc index 73494a7f2..1b5671fcb 100644 --- a/src/xenia/gpu/draw_util.cc +++ b/src/xenia/gpu/draw_util.cc @@ -399,16 +399,11 @@ void GetHostViewportInfo(const RegisterFile& regs, 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; @@ -511,8 +506,8 @@ void GetHostViewportInfo(const RegisterFile& regs, // 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. @@ -877,10 +872,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); diff --git a/src/xenia/gpu/shader_interpreter.cc b/src/xenia/gpu/shader_interpreter.cc index 9a1342aca..aaf7b2ebd 100644 --- a/src/xenia/gpu/shader_interpreter.cc +++ b/src/xenia/gpu/shader_interpreter.cc @@ -346,16 +346,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: { @@ -366,14 +368,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: { @@ -419,14 +421,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]; } @@ -478,32 +480,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]; @@ -529,21 +537,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 @@ -557,19 +565,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: { @@ -590,14 +598,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: { @@ -702,7 +709,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). @@ -713,23 +721,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); @@ -795,22 +806,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: @@ -829,11 +838,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: { @@ -845,7 +854,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: { @@ -862,10 +871,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); @@ -891,11 +901,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(); diff --git a/src/xenia/gpu/trace_viewer.cc b/src/xenia/gpu/trace_viewer.cc index 178c30fc9..754942411 100644 --- a/src/xenia/gpu/trace_viewer.cc +++ b/src/xenia/gpu/trace_viewer.cc @@ -1066,7 +1066,7 @@ void ProgressBar(float frac, float width, float height = 0, if (height == 0) { height = ImGui::GetTextLineHeightWithSpacing(); } - frac = xe::saturate_unsigned(frac); + frac = xe::saturate(frac); const auto fontAtlas = ImGui::GetIO().Fonts; diff --git a/src/xenia/gpu/xenos.cc b/src/xenia/gpu/xenos.cc index ce7f6177d..a2ab6cea5 100644 --- a/src/xenia/gpu/xenos.cc +++ b/src/xenia/gpu/xenos.cc @@ -27,7 +27,7 @@ namespace xenos { float PWLGammaToLinear(float gamma) { // Not found in game executables, so just using the logic similar to that in // the Source Engine. - gamma = xe::saturate_unsigned(gamma); + gamma = xe::saturate(gamma); float scale, offset; // While the compiled code for linear to gamma conversion uses `vcmpgtfp // constant, value` comparison (constant > value, or value < constant), it's @@ -68,7 +68,7 @@ float PWLGammaToLinear(float gamma) { } float LinearToPWLGamma(float linear) { - linear = xe::saturate_unsigned(linear); + linear = xe::saturate(linear); float scale, offset; // While the compiled code uses `vcmpgtfp constant, value` comparison // (constant > value, or value < constant), it's preferable to use `value >= diff --git a/src/xenia/ui/immediate_drawer.cc b/src/xenia/ui/immediate_drawer.cc index fb00be77f..4d3c6bb4e 100644 --- a/src/xenia/ui/immediate_drawer.cc +++ b/src/xenia/ui/immediate_drawer.cc @@ -12,6 +12,7 @@ #include #include "xenia/base/assert.h" +#include "xenia/base/math.h" #include "xenia/ui/graphics_util.h" #include "xenia/ui/presenter.h" @@ -67,24 +68,19 @@ bool ImmediateDrawer::ScissorToRenderTarget(const ImmediateDraw& immediate_draw, } float render_target_width_float = float(render_target_width); float render_target_height_float = float(render_target_height); - // Scale to render target coordinates, drop NaNs (by doing - // std::max(0.0f, variable) in this argument order), and clamp to the render + // Scale to render target coordinates, drop NaNs, and clamp to the render // target size, below which the values are representable as 16p8 fixed-point. float scale_x = render_target_width / coordinate_space_width(); float scale_y = render_target_height / coordinate_space_height(); - float x0_float = - std::min(render_target_width_float, - std::max(0.0f, immediate_draw.scissor_left * scale_x)); - float y0_float = - std::min(render_target_height_float, - std::max(0.0f, immediate_draw.scissor_top * scale_y)); + float x0_float = xe::clamp_float(immediate_draw.scissor_left * scale_x, 0.0f, + render_target_width_float); + float y0_float = xe::clamp_float(immediate_draw.scissor_top * scale_y, 0.0f, + render_target_height_float); // Also make sure the size is non-negative. - float x1_float = - std::min(render_target_width_float, - std::max(x0_float, immediate_draw.scissor_right * scale_x)); - float y1_float = - std::min(render_target_height_float, - std::max(y0_float, immediate_draw.scissor_bottom * scale_y)); + float x1_float = xe::clamp_float(immediate_draw.scissor_right * scale_x, + x0_float, render_target_width_float); + float y1_float = xe::clamp_float(immediate_draw.scissor_bottom * scale_y, + y0_float, render_target_height_float); // Top-left - include .5 (0.128 treated as 0 covered, 0.129 as 0 not covered). int32_t x0 = (FloatToD3D11Fixed16p8(x0_float) + 127) >> 8; int32_t y0 = (FloatToD3D11Fixed16p8(y0_float) + 127) >> 8; diff --git a/src/xenia/ui/window_android.cc b/src/xenia/ui/window_android.cc index d67d478d1..8de82f400 100644 --- a/src/xenia/ui/window_android.cc +++ b/src/xenia/ui/window_android.cc @@ -153,16 +153,16 @@ bool AndroidWindow::OnActivitySurfaceMotionEvent(jobject event) { // with out-of-bounds coordinates), when moving the mouse outside the // View, or when starting moving the mouse when the pointer was previously // outside the View in some cases. - int32_t mouse_x = int32_t( - std::min(float(GetActualPhysicalWidth()), - std::max(0.0f, jni_env->CallFloatMethod( - event, jni_ids.motion_event_get_x, 0))) + - 0.5f); - int32_t mouse_y = int32_t( - std::min(float(GetActualPhysicalHeight()), - std::max(0.0f, jni_env->CallFloatMethod( - event, jni_ids.motion_event_get_y, 0))) + - 0.5f); + int32_t mouse_x = + int32_t(xe::clamp_float(jni_env->CallFloatMethod( + event, jni_ids.motion_event_get_x, 0), + 0.0f, float(GetActualPhysicalWidth())) + + 0.5f); + int32_t mouse_y = + int32_t(xe::clamp_float(jni_env->CallFloatMethod( + event, jni_ids.motion_event_get_y, 0), + 0.0f, float(GetActualPhysicalHeight())) + + 0.5f); static const MouseEvent::Button kMouseEventButtons[] = { MouseEvent::Button::kLeft, MouseEvent::Button::kRight, MouseEvent::Button::kMiddle, MouseEvent::Button::kX1,