Pica: Rewrite Fix12P4 and move to pica_types.h

This commit is contained in:
Jannik Vogel 2016-04-29 18:21:18 +02:00
parent 2d848836c1
commit 5d7dbf5807
2 changed files with 156 additions and 70 deletions

View File

@ -5,12 +5,154 @@
#pragma once #pragma once
#include <cmath> #include <cmath>
#include <cstdint>
#include <cstring> #include <cstring>
#include "common/common_types.h" #include "common/common_types.h"
namespace Pica { 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<int16_t>(((int_val * 16) & IntMask()) | (frac_val & FracMask()))};
}
static Fix12P4 FromFloat(float float_val) noexcept {
return Fix12P4{static_cast<int16_t>(::round(float_val * 16.0f))};
}
static constexpr Fix12P4 Zero() noexcept {
return Fix12P4{static_cast<int16_t>(0)};
}
static constexpr int16_t FracMask() noexcept {
return static_cast<int16_t>(0xF);
}
static constexpr int16_t IntMask() noexcept {
return static_cast<int16_t>(~0xF);
}
constexpr int16_t Int() const noexcept {
return static_cast<int16_t>((value & IntMask()) / 16);
}
constexpr uint16_t Frac() const noexcept {
return static_cast<uint16_t>(value & FracMask());
}
constexpr Fix12P4 Ceil() const noexcept {
return Fix12P4{static_cast<int16_t>(value + FracMask())}.Floor();
}
constexpr Fix12P4 Floor() const noexcept {
return Fix12P4{static_cast<int16_t>(value & IntMask())};
}
constexpr explicit operator int16_t() const noexcept {
return value;
}
constexpr Fix12P4 operator+() const noexcept {
return Fix12P4{static_cast<int16_t>(+value)};
}
constexpr Fix12P4 operator-() const noexcept {
return Fix12P4{static_cast<int16_t>(-value)};
}
constexpr Fix12P4 operator+(const Fix12P4& other) const noexcept {
return Fix12P4{static_cast<int16_t>(value + other.value)};
}
constexpr Fix12P4 operator-(const Fix12P4& other) const noexcept {
return Fix12P4{static_cast<int16_t>(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<int16_t>((left * right) / 16);
}
static constexpr int16_t Divide(int16_t left, int16_t right) noexcept {
return static_cast<int16_t>((left * 16) / right);
}
int16_t value = 0;
};
/** /**
* Template class for converting arbitrary Pica float types to IEEE 754 32-bit single-precision * Template class for converting arbitrary Pica float types to IEEE 754 32-bit single-precision
* floating point. * floating point.

View File

@ -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<s16>(((intVal * 16) & IntMask()) | (fracVal & FracMask())));
}
static Fix12P4 FromFloat(float fltVal) {
return FromRaw(static_cast<s16>(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<s16>((val & IntMask()) / 16);
}
u16 Frac() const {
return static_cast<u16>(val & FracMask());
}
Fix12P4 Ceil() const {
return FromRaw(static_cast<s16>(val + FracMask())).Floor();
}
Fix12P4 Floor() const {
return FromRaw(static_cast<s16>(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<s16>((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. * Calculate signed area of the triangle spanned by the three argument vertices.
* The sign denotes an orientation. * The sign denotes an orientation.
@ -333,8 +266,12 @@ private:
static int SignedArea (const Math::Vec2<Fix12P4>& vtx1, static int SignedArea (const Math::Vec2<Fix12P4>& vtx1,
const Math::Vec2<Fix12P4>& vtx2, const Math::Vec2<Fix12P4>& vtx2,
const Math::Vec2<Fix12P4>& vtx3) { const Math::Vec2<Fix12P4>& vtx3) {
const auto vec1 = Math::MakeVec(vtx2 - vtx1, 0); const auto vec1 = Math::MakeVec<int>(static_cast<s16>(vtx2.x) - static_cast<s16>(vtx1.x),
const auto vec2 = Math::MakeVec(vtx3 - vtx1, 0); static_cast<s16>(vtx2.y) - static_cast<s16>(vtx1.y),
0);
const auto vec2 = Math::MakeVec<int>(static_cast<s16>(vtx3.x) - static_cast<s16>(vtx1.x),
static_cast<s16>(vtx3.y) - static_cast<s16>(vtx1.y),
0);
// TODO: There is a very small chance this will overflow for sizeof(int) == 4 // TODO: There is a very small chance this will overflow for sizeof(int) == 4
return Math::Cross(vec1, vec2).z; return Math::Cross(vec1, vec2).z;
}; };
@ -355,6 +292,9 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0,
const auto& output_merger = regs.output_merger; const auto& output_merger = regs.output_merger;
MICROPROFILE_SCOPE(GPU_Rasterization); 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 // vertex positions in rasterizer coordinates
static auto FloatToFix = [](float24 flt) { static auto FloatToFix = [](float24 flt) {
// TODO: Rounding in Fix12P4::FromFloat is necessary to prevent garbage pixels at // TODO: Rounding in Fix12P4::FromFloat is necessary to prevent garbage pixels at
@ -420,7 +360,11 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0,
} else { } else {
// check if vertex is on our left => right side // check if vertex is on our left => right side
// TODO: Not sure how likely this is to overflow // 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<s16>(line2.x) - static_cast<s16>(line1.x);
int line_dy = static_cast<s16>(line2.y) - static_cast<s16>(line1.y);
int line_vtx_dx = static_cast<s16>(vtx.x) - static_cast<s16>(line1.x);
int line_vtx_dy = static_cast<s16>(vtx.y) - static_cast<s16>(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; int bias0 = IsRightSideOrFlatBottomEdge(vtxpos[0].xy(), vtxpos[1].xy(), vtxpos[2].xy()) ? -1 : 0;