pica_types: Replace float24/20/16 with a template class.

This commit is contained in:
bunnei 2015-12-16 23:23:50 -05:00
parent d171822dce
commit a949fd5f25
5 changed files with 83 additions and 117 deletions

View file

@ -59,12 +59,12 @@ static void InitScreenCoordinates(OutputVertex& vtx)
} viewport; } viewport;
const auto& regs = g_state.regs; const auto& regs = g_state.regs;
viewport.halfsize_x = float24::FromRawFloat24(regs.viewport_size_x); viewport.halfsize_x = float24::FromRaw(regs.viewport_size_x);
viewport.halfsize_y = float24::FromRawFloat24(regs.viewport_size_y); viewport.halfsize_y = float24::FromRaw(regs.viewport_size_y);
viewport.offset_x = float24::FromFloat32(static_cast<float>(regs.viewport_corner.x)); viewport.offset_x = float24::FromFloat32(static_cast<float>(regs.viewport_corner.x));
viewport.offset_y = float24::FromFloat32(static_cast<float>(regs.viewport_corner.y)); viewport.offset_y = float24::FromFloat32(static_cast<float>(regs.viewport_corner.y));
viewport.zscale = float24::FromRawFloat24(regs.viewport_depth_range); viewport.zscale = float24::FromRaw(regs.viewport_depth_range);
viewport.offset_z = float24::FromRawFloat24(regs.viewport_depth_far_plane); viewport.offset_z = float24::FromRaw(regs.viewport_depth_far_plane);
float24 inv_w = float24::FromFloat32(1.f) / vtx.pos.w; float24 inv_w = float24::FromFloat32(1.f) / vtx.pos.w;
vtx.color *= inv_w; vtx.color *= inv_w;

View file

@ -98,10 +98,10 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
Math::Vec4<float24>& attribute = g_state.vs.default_attributes[setup.index]; Math::Vec4<float24>& attribute = g_state.vs.default_attributes[setup.index];
// NOTE: The destination component order indeed is "backwards" // NOTE: The destination component order indeed is "backwards"
attribute.w = float24::FromRawFloat24(default_attr_write_buffer[0] >> 8); attribute.w = float24::FromRaw(default_attr_write_buffer[0] >> 8);
attribute.z = float24::FromRawFloat24(((default_attr_write_buffer[0] & 0xFF) << 16) | ((default_attr_write_buffer[1] >> 16) & 0xFFFF)); attribute.z = float24::FromRaw(((default_attr_write_buffer[0] & 0xFF) << 16) | ((default_attr_write_buffer[1] >> 16) & 0xFFFF));
attribute.y = float24::FromRawFloat24(((default_attr_write_buffer[1] & 0xFFFF) << 8) | ((default_attr_write_buffer[2] >> 24) & 0xFF)); attribute.y = float24::FromRaw(((default_attr_write_buffer[1] & 0xFFFF) << 8) | ((default_attr_write_buffer[2] >> 24) & 0xFF));
attribute.x = float24::FromRawFloat24(default_attr_write_buffer[2] & 0xFFFFFF); attribute.x = float24::FromRaw(default_attr_write_buffer[2] & 0xFFFFFF);
LOG_TRACE(HW_GPU, "Set default VS attribute %x to (%f %f %f %f)", (int)setup.index, LOG_TRACE(HW_GPU, "Set default VS attribute %x to (%f %f %f %f)", (int)setup.index,
attribute.x.ToFloat32(), attribute.y.ToFloat32(), attribute.z.ToFloat32(), attribute.x.ToFloat32(), attribute.y.ToFloat32(), attribute.z.ToFloat32(),
@ -418,10 +418,10 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
uniform[3 - i] = float24::FromFloat32(*(float*)(&uniform_write_buffer[i])); uniform[3 - i] = float24::FromFloat32(*(float*)(&uniform_write_buffer[i]));
} else { } else {
// TODO: Untested // TODO: Untested
uniform.w = float24::FromRawFloat24(uniform_write_buffer[0] >> 8); uniform.w = float24::FromRaw(uniform_write_buffer[0] >> 8);
uniform.z = float24::FromRawFloat24(((uniform_write_buffer[0] & 0xFF)<<16) | ((uniform_write_buffer[1] >> 16) & 0xFFFF)); uniform.z = float24::FromRaw(((uniform_write_buffer[0] & 0xFF) << 16) | ((uniform_write_buffer[1] >> 16) & 0xFFFF));
uniform.y = float24::FromRawFloat24(((uniform_write_buffer[1] & 0xFFFF)<<8) | ((uniform_write_buffer[2] >> 24) & 0xFF)); uniform.y = float24::FromRaw(((uniform_write_buffer[1] & 0xFFFF) << 8) | ((uniform_write_buffer[2] >> 24) & 0xFF));
uniform.x = float24::FromRawFloat24(uniform_write_buffer[2] & 0xFFFFFF); uniform.x = float24::FromRaw(uniform_write_buffer[2] & 0xFFFFFF);
} }
LOG_TRACE(HW_GPU, "Set uniform %x to (%f %f %f %f)", (int)uniform_setup.index, LOG_TRACE(HW_GPU, "Set uniform %x to (%f %f %f %f)", (int)uniform_setup.index,

View file

@ -4,35 +4,51 @@
#pragma once #pragma once
#include <cstring>
#include "common/common_types.h" #include "common/common_types.h"
namespace Pica { namespace Pica {
struct float24 { /**
static float24 FromFloat32(float val) { * Template class for converting arbitrary Pica float types to IEEE 754 32-bit single-precision
float24 ret; * floating point.
*
* When decoding, format is as follows:
* - The first `M` bits are the mantissa
* - The next `E` bits are the exponent
* - The last bit is the sign bit
*
* @todo Verify on HW if this conversion is sufficently accurate.
*/
template<unsigned M, unsigned E>
struct Float {
public:
static Float<M, E> FromFloat32(float val) {
Float<M, E> ret;
ret.value = val; ret.value = val;
return ret; return ret;
} }
// 16 bit mantissa, 7 bit exponent, 1 bit sign static Float<M, E> FromRaw(u32 hex) {
// TODO: No idea if this works as intended Float<M, E> res;
static float24 FromRawFloat24(u32 hex) {
float24 ret; const int width = M + E + 1;
if ((hex & 0xFFFFFF) == 0) { const int bias = 128 - (1 << (E - 1));
ret.value = 0; const int exponent = (hex >> M) & ((1 << E) - 1);
} else { const unsigned mantissa = hex & ((1 << M) - 1);
u32 mantissa = hex & 0xFFFF;
u32 exponent = (hex >> 16) & 0x7F; if (hex & ((1 << (width - 1)) - 1))
u32 sign = hex >> 23; hex = ((hex >> (E + M)) << 31) | (mantissa << (23 - M)) | ((exponent + bias) << 23);
ret.value = std::pow(2.0f, (float)exponent-63.0f) * (1.0f + mantissa * std::pow(2.0f, -16.f)); else
if (sign) hex = ((hex >> (E + M)) << 31);
ret.value = -ret.value;
} std::memcpy(&res.value, &hex, sizeof(float));
return ret;
return res;
} }
static float24 Zero() { static Float<M, E> Zero() {
return FromFloat32(0.f); return FromFloat32(0.f);
} }
@ -41,27 +57,27 @@ struct float24 {
return value; return value;
} }
float24 operator * (const float24& flt) const { Float<M, E> operator * (const Float<M, E>& flt) const {
if ((this->value == 0.f && !std::isnan(flt.value)) || if ((this->value == 0.f && !std::isnan(flt.value)) ||
(flt.value == 0.f && !std::isnan(this->value))) (flt.value == 0.f && !std::isnan(this->value)))
// PICA gives 0 instead of NaN when multiplying by inf // PICA gives 0 instead of NaN when multiplying by inf
return Zero(); return Zero();
return float24::FromFloat32(ToFloat32() * flt.ToFloat32()); return Float<M, E>::FromFloat32(ToFloat32() * flt.ToFloat32());
} }
float24 operator / (const float24& flt) const { Float<M, E> operator / (const Float<M, E>& flt) const {
return float24::FromFloat32(ToFloat32() / flt.ToFloat32()); return Float<M, E>::FromFloat32(ToFloat32() / flt.ToFloat32());
} }
float24 operator + (const float24& flt) const { Float<M, E> operator + (const Float<M, E>& flt) const {
return float24::FromFloat32(ToFloat32() + flt.ToFloat32()); return Float<M, E>::FromFloat32(ToFloat32() + flt.ToFloat32());
} }
float24 operator - (const float24& flt) const { Float<M, E> operator - (const Float<M, E>& flt) const {
return float24::FromFloat32(ToFloat32() - flt.ToFloat32()); return Float<M, E>::FromFloat32(ToFloat32() - flt.ToFloat32());
} }
float24& operator *= (const float24& flt) { Float<M, E>& operator *= (const Float<M, E>& flt) {
if ((this->value == 0.f && !std::isnan(flt.value)) || if ((this->value == 0.f && !std::isnan(flt.value)) ||
(flt.value == 0.f && !std::isnan(this->value))) (flt.value == 0.f && !std::isnan(this->value)))
// PICA gives 0 instead of NaN when multiplying by inf // PICA gives 0 instead of NaN when multiplying by inf
@ -70,111 +86,61 @@ struct float24 {
return *this; return *this;
} }
float24& operator /= (const float24& flt) { Float<M, E>& operator /= (const Float<M, E>& flt) {
value /= flt.ToFloat32(); value /= flt.ToFloat32();
return *this; return *this;
} }
float24& operator += (const float24& flt) { Float<M, E>& operator += (const Float<M, E>& flt) {
value += flt.ToFloat32(); value += flt.ToFloat32();
return *this; return *this;
} }
float24& operator -= (const float24& flt) { Float<M, E>& operator -= (const Float<M, E>& flt) {
value -= flt.ToFloat32(); value -= flt.ToFloat32();
return *this; return *this;
} }
float24 operator - () const { Float<M, E> operator - () const {
return float24::FromFloat32(-ToFloat32()); return Float<M, E>::FromFloat32(-ToFloat32());
} }
bool operator < (const float24& flt) const { bool operator < (const Float<M, E>& flt) const {
return ToFloat32() < flt.ToFloat32(); return ToFloat32() < flt.ToFloat32();
} }
bool operator > (const float24& flt) const { bool operator > (const Float<M, E>& flt) const {
return ToFloat32() > flt.ToFloat32(); return ToFloat32() > flt.ToFloat32();
} }
bool operator >= (const float24& flt) const { bool operator >= (const Float<M, E>& flt) const {
return ToFloat32() >= flt.ToFloat32(); return ToFloat32() >= flt.ToFloat32();
} }
bool operator <= (const float24& flt) const { bool operator <= (const Float<M, E>& flt) const {
return ToFloat32() <= flt.ToFloat32(); return ToFloat32() <= flt.ToFloat32();
} }
bool operator == (const float24& flt) const { bool operator == (const Float<M, E>& flt) const {
return ToFloat32() == flt.ToFloat32(); return ToFloat32() == flt.ToFloat32();
} }
bool operator != (const float24& flt) const { bool operator != (const Float<M, E>& flt) const {
return ToFloat32() != flt.ToFloat32(); return ToFloat32() != flt.ToFloat32();
} }
private: private:
static const unsigned MASK = (1 << (M + E + 1)) - 1;
static const unsigned MANTISSA_MASK = (1 << M) - 1;
static const unsigned EXPONENT_MASK = (1 << E) - 1;
// Stored as a regular float, merely for convenience // Stored as a regular float, merely for convenience
// TODO: Perform proper arithmetic on this! // TODO: Perform proper arithmetic on this!
float value; float value;
}; };
static_assert(sizeof(float24) == sizeof(float), "Shader JIT assumes float24 is implemented as a 32-bit float"); using float24 = Float<16, 7>;
using float20 = Float<12, 7>;
struct float16 { using float16 = Float<10, 5>;
// 10 bit mantissa, 5 bit exponent, 1 bit sign
// TODO: No idea if this works as intended
static float16 FromRawFloat16(u32 hex) {
float16 ret;
if ((hex & 0xFFFF) == 0) {
ret.value = 0;
} else {
u32 mantissa = hex & 0x3FF;
u32 exponent = (hex >> 10) & 0x1F;
u32 sign = (hex >> 15) & 1;
ret.value = std::pow(2.0f, (float)exponent - 15.0f) * (1.0f + mantissa * std::pow(2.0f, -10.f));
if (sign)
ret.value = -ret.value;
}
return ret;
}
float ToFloat32() const {
return value;
}
private:
// Stored as a regular float, merely for convenience
// TODO: Perform proper arithmetic on this!
float value;
};
struct float20 {
// 12 bit mantissa, 7 bit exponent, 1 bit sign
// TODO: No idea if this works as intended
static float20 FromRawFloat20(u32 hex) {
float20 ret;
if ((hex & 0xFFFFF) == 0) {
ret.value = 0;
} else {
u32 mantissa = hex & 0xFFF;
u32 exponent = (hex >> 12) & 0x7F;
u32 sign = (hex >> 19) & 1;
ret.value = std::pow(2.0f, (float)exponent - 63.0f) * (1.0f + mantissa * std::pow(2.0f, -12.f));
if (sign)
ret.value = -ret.value;
}
return ret;
}
float ToFloat32() const {
return value;
}
private:
// Stored as a regular float, merely for convenience
// TODO: Perform proper arithmetic on this!
float value;
};
} // namespace Pica } // namespace Pica

View file

@ -810,8 +810,8 @@ void RasterizerOpenGL::SyncCullMode() {
} }
void RasterizerOpenGL::SyncDepthModifiers() { void RasterizerOpenGL::SyncDepthModifiers() {
float depth_scale = -Pica::float24::FromRawFloat24(Pica::g_state.regs.viewport_depth_range).ToFloat32(); float depth_scale = -Pica::float24::FromRaw(Pica::g_state.regs.viewport_depth_range).ToFloat32();
float depth_offset = Pica::float24::FromRawFloat24(Pica::g_state.regs.viewport_depth_far_plane).ToFloat32() / 2.0f; float depth_offset = Pica::float24::FromRaw(Pica::g_state.regs.viewport_depth_far_plane).ToFloat32() / 2.0f;
// TODO: Implement scale modifier // TODO: Implement scale modifier
uniform_block_data.data.depth_offset = depth_offset; uniform_block_data.data.depth_offset = depth_offset;
@ -948,9 +948,9 @@ void RasterizerOpenGL::SyncLightAmbient(int light_index) {
void RasterizerOpenGL::SyncLightPosition(int light_index) { void RasterizerOpenGL::SyncLightPosition(int light_index) {
std::array<GLfloat, 3> position = { std::array<GLfloat, 3> position = {
Pica::float16::FromRawFloat16(Pica::g_state.regs.lighting.light[light_index].x).ToFloat32(), Pica::float16::FromRaw(Pica::g_state.regs.lighting.light[light_index].x).ToFloat32(),
Pica::float16::FromRawFloat16(Pica::g_state.regs.lighting.light[light_index].y).ToFloat32(), Pica::float16::FromRaw(Pica::g_state.regs.lighting.light[light_index].y).ToFloat32(),
Pica::float16::FromRawFloat16(Pica::g_state.regs.lighting.light[light_index].z).ToFloat32() }; Pica::float16::FromRaw(Pica::g_state.regs.lighting.light[light_index].z).ToFloat32() };
if (position != uniform_block_data.data.light_src[light_index].position) { if (position != uniform_block_data.data.light_src[light_index].position) {
uniform_block_data.data.light_src[light_index].position = position; uniform_block_data.data.light_src[light_index].position = position;
@ -962,8 +962,8 @@ void RasterizerOpenGL::SyncDrawState() {
const auto& regs = Pica::g_state.regs; const auto& regs = Pica::g_state.regs;
// Sync the viewport // Sync the viewport
GLsizei viewport_width = (GLsizei)Pica::float24::FromRawFloat24(regs.viewport_size_x).ToFloat32() * 2; GLsizei viewport_width = (GLsizei)Pica::float24::FromRaw(regs.viewport_size_x).ToFloat32() * 2;
GLsizei viewport_height = (GLsizei)Pica::float24::FromRawFloat24(regs.viewport_size_y).ToFloat32() * 2; GLsizei viewport_height = (GLsizei)Pica::float24::FromRaw(regs.viewport_size_y).ToFloat32() * 2;
// OpenGL uses different y coordinates, so negate corner offset and flip origin // OpenGL uses different y coordinates, so negate corner offset and flip origin
// TODO: Ensure viewport_corner.x should not be negated or origin flipped // TODO: Ensure viewport_corner.x should not be negated or origin flipped

View file

@ -83,8 +83,8 @@ struct PicaShaderConfig {
res.lighting.light[light_index].directional = light.directional != 0; res.lighting.light[light_index].directional = light.directional != 0;
res.lighting.light[light_index].two_sided_diffuse = light.two_sided_diffuse != 0; res.lighting.light[light_index].two_sided_diffuse = light.two_sided_diffuse != 0;
res.lighting.light[light_index].dist_atten_enable = regs.lighting.IsDistAttenEnabled(num); res.lighting.light[light_index].dist_atten_enable = regs.lighting.IsDistAttenEnabled(num);
res.lighting.light[light_index].dist_atten_bias = Pica::float20::FromRawFloat20(light.dist_atten_bias).ToFloat32(); res.lighting.light[light_index].dist_atten_bias = Pica::float20::FromRaw(light.dist_atten_bias).ToFloat32();
res.lighting.light[light_index].dist_atten_scale = Pica::float20::FromRawFloat20(light.dist_atten_scale).ToFloat32(); res.lighting.light[light_index].dist_atten_scale = Pica::float20::FromRaw(light.dist_atten_scale).ToFloat32();
} }
res.lighting.lut_d0.enable = regs.lighting.lut_enable_d0 == 0; res.lighting.lut_d0.enable = regs.lighting.lut_enable_d0 == 0;