diff --git a/src/video_core/pica_types.h b/src/video_core/pica_types.h index 3b7bfbdca..1e512cf19 100644 --- a/src/video_core/pica_types.h +++ b/src/video_core/pica_types.h @@ -5,12 +5,154 @@ #pragma once #include +#include #include #include "common/common_types.h" namespace Pica { +/** + * Class for storing and operating on Fixed point types using: + * + * - 12 integer bits + * - 4 fraction bits + * - Negative values in two's complement + * + * @todo Verify on HW where and how this is used. + * @todo Create a template for more fixed point types and use this elsewhere too. + */ +class Fix12P4 final { +public: + constexpr Fix12P4() = default; + constexpr Fix12P4(const Fix12P4&) = default; + constexpr Fix12P4(Fix12P4&&) = default; + constexpr explicit Fix12P4(int16_t raw) noexcept : value(raw) {} + + Fix12P4& operator=(const Fix12P4&) = default; + Fix12P4& operator=(Fix12P4&&) = default; + + static constexpr Fix12P4 FromInt(int16_t int_val, uint16_t frac_val = 0) noexcept { + return Fix12P4{static_cast(((int_val * 16) & IntMask()) | (frac_val & FracMask()))}; + } + + static Fix12P4 FromFloat(float float_val) noexcept { + return Fix12P4{static_cast(::round(float_val * 16.0f))}; + } + + static constexpr Fix12P4 Zero() noexcept { + return Fix12P4{static_cast(0)}; + } + + static constexpr int16_t FracMask() noexcept { + return static_cast(0xF); + } + + static constexpr int16_t IntMask() noexcept { + return static_cast(~0xF); + } + + constexpr int16_t Int() const noexcept { + return static_cast((value & IntMask()) / 16); + } + + constexpr uint16_t Frac() const noexcept { + return static_cast(value & FracMask()); + } + + constexpr Fix12P4 Ceil() const noexcept { + return Fix12P4{static_cast(value + FracMask())}.Floor(); + } + + constexpr Fix12P4 Floor() const noexcept { + return Fix12P4{static_cast(value & IntMask())}; + } + + constexpr explicit operator int16_t() const noexcept { + return value; + } + + constexpr Fix12P4 operator+() const noexcept { + return Fix12P4{static_cast(+value)}; + } + + constexpr Fix12P4 operator-() const noexcept { + return Fix12P4{static_cast(-value)}; + } + + constexpr Fix12P4 operator+(const Fix12P4& other) const noexcept { + return Fix12P4{static_cast(value + other.value)}; + } + + constexpr Fix12P4 operator-(const Fix12P4& other) const noexcept { + return Fix12P4{static_cast(value - other.value)}; + } + + constexpr Fix12P4 operator*(const Fix12P4& other) const noexcept { + return Fix12P4{Multiply(value, other.value)}; + } + + constexpr Fix12P4 operator/(const Fix12P4& other) const noexcept { + return Fix12P4{Divide(value, other.value)}; + } + + Fix12P4& operator+=(const Fix12P4& other) noexcept { + value += other.value; + return *this; + } + + Fix12P4& operator-=(const Fix12P4& other) noexcept { + value -= other.value; + return *this; + } + + Fix12P4& operator*=(const Fix12P4& other) noexcept { + value = Multiply(value, other.value); + return *this; + } + + Fix12P4& operator/=(const Fix12P4& other) noexcept { + value = Divide(value, other.value); + return *this; + } + + friend constexpr bool operator<(const Fix12P4& left, const Fix12P4& right) noexcept { + return left.value < right.value; + } + + friend constexpr bool operator>(const Fix12P4& left, const Fix12P4& right) noexcept { + return operator<(right, left); + } + + friend constexpr bool operator>=(const Fix12P4& left, const Fix12P4& right) noexcept { + return !operator<(left, right); + } + + friend constexpr bool operator<=(const Fix12P4& left, const Fix12P4& right) noexcept { + return !operator<(right, left); + } + + friend constexpr bool operator==(const Fix12P4& left, const Fix12P4& right) noexcept { + return left.value == right.value; + } + + friend constexpr bool operator!=(const Fix12P4& left, const Fix12P4& right) noexcept { + return !operator==(left, right); + } + +private: + static constexpr int16_t Multiply(int16_t left, int16_t right) noexcept { + return static_cast((left * right) / 16); + } + + static constexpr int16_t Divide(int16_t left, int16_t right) noexcept { + return static_cast((left * 16) / right); + } + + int16_t value = 0; +}; + + /** * Template class for converting arbitrary Pica float types to IEEE 754 32-bit single-precision * floating point. diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp index 9f1bdf53f..9e45625db 100644 --- a/src/video_core/rasterizer.cpp +++ b/src/video_core/rasterizer.cpp @@ -257,73 +257,6 @@ static u8 PerformStencilAction(Regs::StencilAction action, u8 old_stencil, u8 re } } -// NOTE: Assuming that rasterizer coordinates are 12.4 fixed-point values -struct Fix12P4 { - Fix12P4() {} - - static Fix12P4 FromRaw(s16 val) { - Fix12P4 res; - res.val = val; - return res; - } - - static Fix12P4 FromInt(s16 intVal, u16 fracVal = 0) { - return FromRaw(static_cast(((intVal * 16) & IntMask()) | (fracVal & FracMask()))); - } - - static Fix12P4 FromFloat(float fltVal) { - return FromRaw(static_cast(round(fltVal * 16.0f))); - } - - static Fix12P4 Zero() { - return FromRaw(0); - } - - static u16 FracMask() { return 0xF; } - static u16 IntMask() { return (u16)~0xF; } - - s16 Int() const { - return static_cast((val & IntMask()) / 16); - } - - u16 Frac() const { - return static_cast(val & FracMask()); - } - - Fix12P4 Ceil() const { - return FromRaw(static_cast(val + FracMask())).Floor(); - } - - Fix12P4 Floor() const { - return FromRaw(static_cast(val & IntMask())); - } - - operator s16() const { - return val; - } - - bool operator < (const Fix12P4& oth) const { - return (s16)*this < (s16)oth; - } - - Fix12P4 operator + (const Fix12P4& oth) const { - return FromRaw(val + oth.val); - } - - Fix12P4 operator / (const Fix12P4& oth) const { - return FromRaw(static_cast((int)val * 16 / (int)oth.val)); - } - - Fix12P4& operator += (const Fix12P4& oth) { - val += oth.val; - return *this; - } - - -private: - s16 val; -}; - /** * Calculate signed area of the triangle spanned by the three argument vertices. * The sign denotes an orientation. @@ -333,8 +266,12 @@ private: static int SignedArea (const Math::Vec2& vtx1, const Math::Vec2& vtx2, const Math::Vec2& vtx3) { - const auto vec1 = Math::MakeVec(vtx2 - vtx1, 0); - const auto vec2 = Math::MakeVec(vtx3 - vtx1, 0); + const auto vec1 = Math::MakeVec(static_cast(vtx2.x) - static_cast(vtx1.x), + static_cast(vtx2.y) - static_cast(vtx1.y), + 0); + const auto vec2 = Math::MakeVec(static_cast(vtx3.x) - static_cast(vtx1.x), + static_cast(vtx3.y) - static_cast(vtx1.y), + 0); // TODO: There is a very small chance this will overflow for sizeof(int) == 4 return Math::Cross(vec1, vec2).z; }; @@ -355,6 +292,9 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, const auto& output_merger = regs.output_merger; MICROPROFILE_SCOPE(GPU_Rasterization); + // NOTE: Assuming that rasterizer coordinates are signed 12.4 fixed-point values. + // This type is what PSP uses, we haven't tested what the 3DS uses. + // vertex positions in rasterizer coordinates static auto FloatToFix = [](float24 flt) { // TODO: Rounding in Fix12P4::FromFloat is necessary to prevent garbage pixels at @@ -420,7 +360,11 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, } else { // check if vertex is on our left => right side // TODO: Not sure how likely this is to overflow - return (int)vtx.x < (int)line1.x + ((int)line2.x - (int)line1.x) * ((int)vtx.y - (int)line1.y) / ((int)line2.y - (int)line1.y); + int line_dx = static_cast(line2.x) - static_cast(line1.x); + int line_dy = static_cast(line2.y) - static_cast(line1.y); + int line_vtx_dx = static_cast(vtx.x) - static_cast(line1.x); + int line_vtx_dy = static_cast(vtx.y) - static_cast(line1.y); + return line_vtx_dx < line_vtx_dy * line_dx / line_dy; } }; int bias0 = IsRightSideOrFlatBottomEdge(vtxpos[0].xy(), vtxpos[1].xy(), vtxpos[2].xy()) ? -1 : 0;