diff --git a/CMakeLists.txt b/CMakeLists.txt
index 55c6a75e42..b4d2e86350 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2049,6 +2049,7 @@ set(GPU_SOURCES
GPU/Common/VertexDecoderCommon.h
GPU/Common/VertexDecoderHandwritten.cpp
GPU/Common/VertexDecoderHandwritten.h
+ GPU/Common/VertexReader.h
GPU/Common/TransformCommon.cpp
GPU/Common/TransformCommon.h
GPU/Common/IndexGenerator.cpp
diff --git a/Common/Math/CrossSIMD.h b/Common/Math/CrossSIMD.h
index 91f8bd4d2b..af834e9f4a 100644
--- a/Common/Math/CrossSIMD.h
+++ b/Common/Math/CrossSIMD.h
@@ -201,6 +201,8 @@ struct Vec4F32 {
static Vec4F32 Load(const float *src) { return Vec4F32{ _mm_loadu_ps(src) }; }
static Vec4F32 LoadAligned(const float *src) { return Vec4F32{ _mm_load_ps(src) }; }
+ static Vec4F32 Load2(const float *src) { return Vec4F32{ _mm_castpd_ps(_mm_load_sd((const double *)src)) }; }
+
static Vec4F32 LoadS8Norm(const int8_t *src) {
__m128i value = _mm_set1_epi32(*((uint32_t *)src));
__m128i value16 = _mm_unpacklo_epi8(value, value);
@@ -209,6 +211,15 @@ struct Vec4F32 {
value32 = _mm_srai_epi32(value32, 24);
return Vec4F32 { _mm_mul_ps(_mm_cvtepi32_ps(value32), _mm_set1_ps(1.0f / 128.0f)) };
}
+
+ static Vec4F32 LoadU8Norm(const uint8_t *src) {
+ __m128i value = _mm_set1_epi32(*((uint32_t *)src));
+ __m128i value16 = _mm_unpacklo_epi8(value, value);
+ __m128i value32 = _mm_unpacklo_epi16(value16, value16);
+ // Sign extension. A bit ugly without SSE4.
+ value32 = _mm_srli_epi32(value32, 24);
+ return Vec4F32{_mm_mul_ps(_mm_cvtepi32_ps(value32), _mm_set1_ps(1.0f / 128.0f))};
+ }
static Vec4F32 LoadS16Norm(const int16_t *src) { // Divides by 32768.0f
__m128i bits = _mm_loadl_epi64((const __m128i*)src);
// Sign extension. A bit ugly without SSE4.
@@ -621,10 +632,19 @@ struct Vec4F32 {
const int16x8_t value16 = vmovl_s8(value);
return Vec4F32 { vcvtq_n_f32_s32(vmovl_s16(vget_low_s16(value16)), 7) };
}
+ static Vec4F32 LoadU8Norm(const uint8_t *src) {
+ const uint8x8_t value = (uint8x8_t)vdup_n_u32(*((uint32_t *)src));
+ const uint16x8_t value16 = vmovl_u8(value);
+ return Vec4F32{ vmulq_f32(vcvtq_f32_u32(vmovl_u16(vget_low_u16(value16))), vdupq_n_f32(1.0f / 255.0f))};
+ }
static Vec4F32 LoadS16Norm(const int16_t *src) { // Divides by 32768.0f
return Vec4F32 { vcvtq_n_f32_s32(vmovl_s16(vld1_s16(src)), 15) };
}
static Vec4F32 LoadAligned(const float *src) { return Vec4F32{ vld1q_f32(src) }; }
+ static Vec4F32 Load2(const float *src) {
+ float32x2_t two = vld1_f32(src);
+ return Vec4F32{ vcombine_f32(two, two) };
+ }
static Vec4F32 LoadConvertS16(const int16_t *src) {
int16x4_t value = vld1_s16(src);
@@ -1113,10 +1133,20 @@ struct Vec4F32 {
static Vec4F32 LoadS8Norm(const int8_t *src) {
return LoadConvertS8(src) * (1.0f / 128.0f);
}
+ static Vec4F32 LoadU8Norm(const uint8_t *src) {
+ return LoadConvertU8(src) * (1.0f / 255.0f);
+ }
static Vec4F32 LoadS16Norm(const int16_t *src) { // Divides by 32768.0f
return LoadConvertS16(src) * (1.0f / 32768.0f);
}
static Vec4F32 LoadAligned(const float *src) { return Vec4F32{ (__m128)__lsx_vld(src, 0) }; }
+ static Vec4F32 Load2(const float *src) {
+ // Load 2 floats and set the last two lanes to 0.0f
+ // NOTE: This is bad, it over-loads.
+ __m128 low = __lsx_vld(src, 0);
+ __m128 zero = __lsx_vldi(0);
+ return Vec4F32{ __lsx_vshuf4i_w(low, zero, 0b11111001) };
+ }
static Vec4F32 LoadConvertS16(const int16_t *src) {
__m128i value = __lsx_vldrepl_d(src, 0);
@@ -1319,10 +1349,10 @@ struct Vec4F32 {
__m128 masked = (__m128)__lsx_vinsgr2vr_w((__m128i)v, 0, 3);
__m128 mul = (__m128)__lsx_vfmul_s(masked, b.v);
// Sum all elements: horizontal add
- __m128 shuf1 = (__m128)__lsx_vshuf4i_w((__m128i)mul, 0b10110001); // Rotate elements
- __m128 sum1 = (__m128)__lsx_vfadd_s(mul, shuf1);
- __m128 shuf2 = (__m128)__lsx_vshuf4i_w((__m128i)sum1, 0b01001110); // Swap pairs
- __m128 sum2 = (__m128)__lsx_vfadd_s(sum1, shuf2);
+ __m128i shuf1 = __lsx_vshuf4i_w((__m128i)mul, 0b10110001); // Rotate elements
+ __m128 sum1 = (__m128)__lsx_vfadd_s(mul, (__m128)shuf1);
+ __m128i shuf2 = __lsx_vshuf4i_w((__m128i)sum1, 0b01001110); // Swap pairs
+ __m128 sum2 = (__m128)__lsx_vfadd_s(sum1, (__m128)shuf2);
float fval;
int ival = __lsx_vpickve2gr_w((__m128i)sum2, 0);
memcpy(&fval, &ival, sizeof(float));
@@ -1332,10 +1362,10 @@ struct Vec4F32 {
float Dot4(Vec4F32 b) {
__m128 mul = (__m128)__lsx_vfmul_s(v, b.v);
// Sum all elements: horizontal add
- __m128 shuf1 = (__m128)__lsx_vshuf4i_w((__m128i)mul, 0b10110001); // Rotate elements
- __m128 sum1 = (__m128)__lsx_vfadd_s(mul, shuf1);
- __m128 shuf2 = (__m128)__lsx_vshuf4i_w((__m128i)sum1, 0b01001110); // Swap pairs
- __m128 sum2 = (__m128)__lsx_vfadd_s(sum1, shuf2);
+ __m128i shuf1 = __lsx_vshuf4i_w((__m128i)mul, 0b10110001); // Rotate elements
+ __m128 sum1 = (__m128)__lsx_vfadd_s(mul, (__m128)shuf1);
+ __m128i shuf2 = __lsx_vshuf4i_w((__m128i)sum1, 0b01001110); // Swap pairs
+ __m128 sum2 = (__m128)__lsx_vfadd_s(sum1, (__m128)shuf2);
float fval;
int ival = __lsx_vpickve2gr_w((__m128i)sum2, 0);
memcpy(&fval, &ival, sizeof(float));
@@ -1607,6 +1637,13 @@ struct Vec4F32 {
}
return temp;
}
+ static Vec4F32 LoadU8Norm(const uint8_t *src) {
+ Vec4F32 temp;
+ for (int i = 0; i < 4; i++) {
+ temp.v[i] = (float)src[i] * (1.0f / 255.0f);
+ }
+ return temp;
+ }
static Vec4F32 LoadS16Norm(const int16_t *src) { // Divides by 32768.0f
Vec4F32 temp;
for (int i = 0; i < 4; i++) {
@@ -1614,6 +1651,8 @@ struct Vec4F32 {
}
return temp;
}
+ static Vec4F32 Load2(const float *src) { return Vec4F32{{ src[0], src[1], 0.0f, 0.0f }}; }
+
void Store(float *dst) { memcpy(dst, v, sizeof(v)); }
void Store2(float *dst) { memcpy(dst, v, sizeof(v[0]) * 2); }
void StoreAligned(float *dst) { memcpy(dst, v, sizeof(v)); }
diff --git a/GPU/Common/SoftwareTransformCommon.cpp b/GPU/Common/SoftwareTransformCommon.cpp
index ba39d4e0e8..cd56bdfd89 100644
--- a/GPU/Common/SoftwareTransformCommon.cpp
+++ b/GPU/Common/SoftwareTransformCommon.cpp
@@ -32,6 +32,7 @@
#include "GPU/Common/SoftwareTransformCommon.h"
#include "GPU/Common/TransformCommon.h"
#include "GPU/Common/VertexDecoderCommon.h"
+#include "GPU/Common/VertexReader.h"
#include "GPU/Common/DrawEngineCommon.h"
#include "GPU/Software/Clipper.h"
diff --git a/GPU/Common/VertexDecoderCommon.cpp b/GPU/Common/VertexDecoderCommon.cpp
index 1817621890..f48e653bef 100644
--- a/GPU/Common/VertexDecoderCommon.cpp
+++ b/GPU/Common/VertexDecoderCommon.cpp
@@ -32,6 +32,7 @@
#include "Core/MIPS/JitCommon/JitCommon.h"
#include "Core/Util/AudioFormat.h" // for clamp_u8
#include "GPU/Common/ShaderCommon.h"
+#include "GPU/Common/VertexReader.h"
#include "GPU/GPUState.h"
#include "GPU/ge_constants.h"
#include "GPU/Math3D.h"
diff --git a/GPU/Common/VertexDecoderCommon.h b/GPU/Common/VertexDecoderCommon.h
index e2278999e2..f7ae78503c 100644
--- a/GPU/Common/VertexDecoderCommon.h
+++ b/GPU/Common/VertexDecoderCommon.h
@@ -44,11 +44,6 @@
#include "Common/FakeEmitter.h"
#endif
-// DecVtxFormat - vertex formats for PC
-// Kind of like a D3D VertexDeclaration.
-// Can write code to easily bind these using OpenGL, or read these manually.
-// No morph support, that is taken care of by the VertexDecoder.
-
// Keep this in 4 bits.
enum {
DEC_NONE,
@@ -68,6 +63,9 @@ enum {
DEC_U16_4,
};
+// DecVtxFormat - vertex formats for PC
+// Kind of like a D3D VertexDeclaration.
+// No morph support, that is taken care of by the VertexDecoder.
struct DecVtxFormat {
u8 w0fmt; u8 w0off; // first 4 weights
u8 w1fmt; u8 w1off; // second 4 weights
@@ -120,204 +118,6 @@ public:
}
};
-// Reads decoded vertex formats in a convenient way. For software transform and debugging.
-class VertexReader {
-public:
- VertexReader(const u8 *base, const DecVtxFormat &decFmt, int vtype) : base_(base), data_(base), decFmt_(decFmt), vtype_(vtype) {}
-
- // TODO: Only reason we might want to still keep the separation of ReadPosThrough and ReadPosNonThrough is
- // in case we need to do some rounding for through mode. I doubt it's needed though.
- void ReadPosAuto(float pos[3]) const {
- // Only DEC_FLOAT_3 is supported.
- const float *f = (const float *)(data_ + decFmt_.posoff);
- pos[0] = f[0];
- pos[1] = f[1];
- if (!isThrough()) {
- pos[2] = f[2];
- } else {
- // Integer value passed in a float. Clamped to 0, 65535.
- pos[2] = f[2];
- }
- }
-
- void ReadPosThrough(float pos[3]) const {
- // Only DEC_FLOAT_3 is supported.
- const float *f = (const float *)(data_ + decFmt_.posoff);
- pos[0] = f[0];
- pos[1] = f[1];
- // Integer value passed in a float. Clamped to 0, 65535 (but where?)
- pos[2] = f[2];
- }
-
- void ReadPosNonThrough(float pos[3]) const {
- // Only DEC_FLOAT_3 is supported.
- const float *f = (const float *)(data_ + decFmt_.posoff);
- memcpy(pos, f, 12);
- }
-
- void ReadNrm(float nrm[3]) const {
- switch (decFmt_.nrmfmt) {
- case DEC_FLOAT_3:
- //memcpy(nrm, data_ + decFmt_.nrmoff, 12);
- {
- const float *f = (const float *)(data_ + decFmt_.nrmoff);
- for (int i = 0; i < 3; i++)
- nrm[i] = f[i];
- }
- break;
- case DEC_S16_3:
- {
- const s16 *s = (const s16 *)(data_ + decFmt_.nrmoff);
- for (int i = 0; i < 3; i++)
- nrm[i] = s[i] * (1.f / 32767.f);
- }
- break;
- case DEC_S8_3:
- {
- const s8 *b = (const s8 *)(data_ + decFmt_.nrmoff);
- for (int i = 0; i < 3; i++)
- nrm[i] = b[i] * (1.f / 127.f);
- }
- break;
- default:
- memset(nrm, 0, sizeof(float) * 3);
- break;
- }
- }
-
- void ReadUV(float uv[2]) const {
- // Only DEC_FLOAT_2 is supported.
- const float *f = (const float *)(data_ + decFmt_.uvoff);
- uv[0] = f[0];
- uv[1] = f[1];
- }
-
- void ReadColor0(float color[4]) const {
- switch (decFmt_.c0fmt) {
- case DEC_U8_4:
- Uint8x4ToFloat4(color, *(const u32 *)(data_ + decFmt_.c0off));
- break;
- case DEC_FLOAT_4:
- memcpy(color, data_ + decFmt_.c0off, 16);
- break;
- default:
- memset(color, 0, sizeof(float) * 4);
- break;
- }
- }
-
- u32 ReadColor0_8888() const {
- switch (decFmt_.c0fmt) {
- case DEC_U8_4:
- {
- const u8 *b = (const u8 *)(data_ + decFmt_.c0off);
- u32 value;
- memcpy(&value, b, 4);
- return value;
- }
- break;
- case DEC_FLOAT_4:
- {
- const float *f = (const float *)(data_ + decFmt_.c0off);
- return Float4ToUint8x4_NoClamp(f);
- }
- break;
- default:
- return 0;
- }
- }
-
- void ReadColor1(float color[3]) const {
- switch (decFmt_.c1fmt) {
- case DEC_U8_4:
- {
- const u8 *b = (const u8 *)(data_ + decFmt_.c1off);
- for (int i = 0; i < 3; i++)
- color[i] = b[i] * (1.f / 255.f);
- }
- break;
- case DEC_FLOAT_4:
- memcpy(color, data_ + decFmt_.c1off, 12);
- break;
- default:
- memset(color, 0, sizeof(float) * 3);
- break;
- }
- }
-
- void ReadWeights(float weights[8]) const {
- const float *f = (const float *)(data_ + decFmt_.w0off);
- const u8 *b = (const u8 *)(data_ + decFmt_.w0off);
- const u16 *s = (const u16 *)(data_ + decFmt_.w0off);
- switch (decFmt_.w0fmt) {
- case DEC_FLOAT_1:
- case DEC_FLOAT_2:
- case DEC_FLOAT_3:
- case DEC_FLOAT_4:
- for (int i = 0; i <= decFmt_.w0fmt - DEC_FLOAT_1; i++)
- weights[i] = f[i];
- break;
- case DEC_U8_1: weights[0] = b[0] * (1.f / 128.f); break;
- case DEC_U8_2: for (int i = 0; i < 2; i++) weights[i] = b[i] * (1.f / 128.f); break;
- case DEC_U8_3: for (int i = 0; i < 3; i++) weights[i] = b[i] * (1.f / 128.f); break;
- case DEC_U8_4: for (int i = 0; i < 4; i++) weights[i] = b[i] * (1.f / 128.f); break;
- case DEC_U16_1: weights[0] = s[0] * (1.f / 32768.f); break;
- case DEC_U16_2: for (int i = 0; i < 2; i++) weights[i] = s[i] * (1.f / 32768.f); break;
- case DEC_U16_3: for (int i = 0; i < 3; i++) weights[i] = s[i] * (1.f / 32768.f); break;
- case DEC_U16_4: for (int i = 0; i < 4; i++) weights[i] = s[i] * (1.f / 32768.f); break;
- default:
- ERROR_LOG_REPORT_ONCE(fmtw0, Log::G3D, "Reader: Unsupported W0 Format %d", decFmt_.w0fmt);
- memset(weights, 0, sizeof(float) * 8);
- break;
- }
-
- f = (const float *)(data_ + decFmt_.w1off);
- b = (const u8 *)(data_ + decFmt_.w1off);
- s = (const u16 *)(data_ + decFmt_.w1off);
- switch (decFmt_.w1fmt) {
- case 0:
- // It's fine for there to be w0 weights but not w1.
- break;
- case DEC_FLOAT_1:
- case DEC_FLOAT_2:
- case DEC_FLOAT_3:
- case DEC_FLOAT_4:
- for (int i = 0; i <= decFmt_.w1fmt - DEC_FLOAT_1; i++)
- weights[i+4] = f[i];
- break;
- case DEC_U8_1: weights[4] = b[0] * (1.f / 128.f); break;
- case DEC_U8_2: for (int i = 0; i < 2; i++) weights[i+4] = b[i] * (1.f / 128.f); break;
- case DEC_U8_3: for (int i = 0; i < 3; i++) weights[i+4] = b[i] * (1.f / 128.f); break;
- case DEC_U8_4: for (int i = 0; i < 4; i++) weights[i+4] = b[i] * (1.f / 128.f); break;
- case DEC_U16_1: weights[4] = s[0] * (1.f / 32768.f); break;
- case DEC_U16_2: for (int i = 0; i < 2; i++) weights[i+4] = s[i] * (1.f / 32768.f); break;
- case DEC_U16_3: for (int i = 0; i < 3; i++) weights[i+4] = s[i] * (1.f / 32768.f); break;
- case DEC_U16_4: for (int i = 0; i < 4; i++) weights[i+4] = s[i] * (1.f / 32768.f); break;
- default:
- memset(weights + 4, 0, sizeof(float) * 4);
- break;
- }
- }
-
- bool hasColor0() const { return decFmt_.c0fmt != 0; }
- bool hasColor1() const { return decFmt_.c1fmt != 0; }
- bool hasNormal() const { return decFmt_.nrmfmt != 0; }
- bool hasUV() const { return decFmt_.uvfmt != 0; }
- bool isThrough() const { return (vtype_ & GE_VTYPE_THROUGH) != 0; }
- void Goto(int index) {
- data_ = base_ + index * decFmt_.stride;
- }
-
-private:
- const u8 *base_;
- const u8 *data_;
- DecVtxFormat decFmt_;
- int vtype_;
-};
-// Debugging utilities
-void PrintDecodedVertex(const VertexReader &vtx);
-
-
class VertexDecoder;
class VertexDecoderJitCache;
diff --git a/GPU/Common/VertexReader.h b/GPU/Common/VertexReader.h
new file mode 100644
index 0000000000..2cc59ba529
--- /dev/null
+++ b/GPU/Common/VertexReader.h
@@ -0,0 +1,239 @@
+#pragma once
+
+#include "Common/Math/CrossSIMD.h"
+#include "GPU/Common/VertexDecoderCommon.h"
+
+// Reads decoded vertex formats in a convenient way. For software transform and debugging.
+class VertexReader {
+public:
+ VertexReader(const u8 *base, const DecVtxFormat &decFmt, int vtype) : base_(base), data_(base), decFmt_(decFmt), vtype_(vtype) {}
+
+ Vec4F32 ReadPosF32() const {
+ // Only DEC_FLOAT_3 is supported.
+ const float *f = (const float *)(data_ + decFmt_.posoff);
+ return Vec4F32::Load(f);
+ }
+
+ // TODO: Only reason we might want to still keep the separation of ReadPosThrough and ReadPosNonThrough is
+ // in case we need to do some rounding for through mode. I doubt it's needed though.
+ void ReadPosAuto(float pos[3]) const {
+ // Only DEC_FLOAT_3 is supported.
+ const float *f = (const float *)(data_ + decFmt_.posoff);
+ pos[0] = f[0];
+ pos[1] = f[1];
+ if (!isThrough()) {
+ pos[2] = f[2];
+ } else {
+ // Integer value passed in a float. Clamped to 0, 65535.
+ pos[2] = f[2];
+ }
+ }
+
+ void ReadPosThrough(float pos[3]) const {
+ // Only DEC_FLOAT_3 is supported.
+ const float *f = (const float *)(data_ + decFmt_.posoff);
+ pos[0] = f[0];
+ pos[1] = f[1];
+ // Integer value passed in a float. Clamped to 0, 65535 (but where?)
+ pos[2] = f[2];
+ }
+
+ void ReadPosNonThrough(float pos[3]) const {
+ // Only DEC_FLOAT_3 is supported.
+ const float *f = (const float *)(data_ + decFmt_.posoff);
+ memcpy(pos, f, 12);
+ }
+
+ // NOTE: The W component of the return value becomes undefined.
+ Vec4F32 ReadNrmF32() const {
+ switch (decFmt_.nrmfmt) {
+ case DEC_FLOAT_3:
+ return Vec4F32::Load((const float *)(data_ + decFmt_.nrmoff));
+ case DEC_S16_3:
+ return Vec4F32::LoadS16Norm((const s16 *)(data_ + decFmt_.nrmoff));
+ case DEC_S8_3:
+ return Vec4F32::LoadS8Norm((const s8 *)(data_ + decFmt_.nrmoff));
+ default:
+ return Vec4F32::Zero();
+ }
+ }
+
+ void ReadNrm(float nrm[3]) const {
+ switch (decFmt_.nrmfmt) {
+ case DEC_FLOAT_3:
+ //memcpy(nrm, data_ + decFmt_.nrmoff, 12);
+ {
+ const float *f = (const float *)(data_ + decFmt_.nrmoff);
+ for (int i = 0; i < 3; i++)
+ nrm[i] = f[i];
+ }
+ break;
+ case DEC_S16_3:
+ {
+ const s16 *s = (const s16 *)(data_ + decFmt_.nrmoff);
+ for (int i = 0; i < 3; i++)
+ nrm[i] = s[i] * (1.f / 32768.f);
+ }
+ break;
+ case DEC_S8_3:
+ {
+ const s8 *b = (const s8 *)(data_ + decFmt_.nrmoff);
+ for (int i = 0; i < 3; i++)
+ nrm[i] = b[i] * (1.f / 128.f);
+ }
+ break;
+ default:
+ memset(nrm, 0, sizeof(float) * 3);
+ break;
+ }
+ }
+
+ void ReadUV(float uv[2]) const {
+ // Only DEC_FLOAT_2 is supported.
+ const float *f = (const float *)(data_ + decFmt_.uvoff);
+ uv[0] = f[0];
+ uv[1] = f[1];
+ }
+
+ Vec4F32 ReadUVF32() const {
+ // Only DEC_FLOAT_2 is supported.
+ // The upper two lanes of the return value become undefined.
+ const float *f = (const float *)(data_ + decFmt_.uvoff);
+ return Vec4F32::Load2(f);
+ }
+
+ void ReadColor0(float color[4]) const {
+ switch (decFmt_.c0fmt) {
+ case DEC_U8_4:
+ Uint8x4ToFloat4(color, *(const u32 *)(data_ + decFmt_.c0off));
+ break;
+ case DEC_FLOAT_4:
+ memcpy(color, data_ + decFmt_.c0off, 16);
+ break;
+ default:
+ memset(color, 0, sizeof(float) * 4);
+ break;
+ }
+ }
+
+ Vec4F32 ReadColorF32() const {
+ switch (decFmt_.c0fmt) {
+ case DEC_U8_4:
+ return Vec4F32::LoadU8Norm((const u8 *)(data_ + decFmt_.c0off));
+ case DEC_FLOAT_4:
+ return Vec4F32::Load((const float *)(data_ + decFmt_.c0off));
+ default:
+ return Vec4F32::Zero();
+ }
+ }
+
+ u32 ReadColor0_8888() const {
+ switch (decFmt_.c0fmt) {
+ case DEC_U8_4:
+ {
+ const u8 *b = (const u8 *)(data_ + decFmt_.c0off);
+ u32 value;
+ memcpy(&value, b, 4);
+ return value;
+ }
+ break;
+ case DEC_FLOAT_4:
+ {
+ const float *f = (const float *)(data_ + decFmt_.c0off);
+ return Float4ToUint8x4_NoClamp(f);
+ }
+ break;
+ default:
+ return 0;
+ }
+ }
+
+ void ReadColor1(float color[3]) const {
+ switch (decFmt_.c1fmt) {
+ case DEC_U8_4:
+ {
+ const u8 *b = (const u8 *)(data_ + decFmt_.c1off);
+ for (int i = 0; i < 3; i++)
+ color[i] = b[i] * (1.f / 255.f);
+ }
+ break;
+ case DEC_FLOAT_4:
+ memcpy(color, data_ + decFmt_.c1off, 12);
+ break;
+ default:
+ memset(color, 0, sizeof(float) * 3);
+ break;
+ }
+ }
+
+ void ReadWeights(float weights[8]) const {
+ const float *f = (const float *)(data_ + decFmt_.w0off);
+ const u8 *b = (const u8 *)(data_ + decFmt_.w0off);
+ const u16 *s = (const u16 *)(data_ + decFmt_.w0off);
+ switch (decFmt_.w0fmt) {
+ case DEC_FLOAT_1:
+ case DEC_FLOAT_2:
+ case DEC_FLOAT_3:
+ case DEC_FLOAT_4:
+ for (int i = 0; i <= decFmt_.w0fmt - DEC_FLOAT_1; i++)
+ weights[i] = f[i];
+ break;
+ case DEC_U8_1: weights[0] = b[0] * (1.f / 128.f); break;
+ case DEC_U8_2: for (int i = 0; i < 2; i++) weights[i] = b[i] * (1.f / 128.f); break;
+ case DEC_U8_3: for (int i = 0; i < 3; i++) weights[i] = b[i] * (1.f / 128.f); break;
+ case DEC_U8_4: for (int i = 0; i < 4; i++) weights[i] = b[i] * (1.f / 128.f); break;
+ case DEC_U16_1: weights[0] = s[0] * (1.f / 32768.f); break;
+ case DEC_U16_2: for (int i = 0; i < 2; i++) weights[i] = s[i] * (1.f / 32768.f); break;
+ case DEC_U16_3: for (int i = 0; i < 3; i++) weights[i] = s[i] * (1.f / 32768.f); break;
+ case DEC_U16_4: for (int i = 0; i < 4; i++) weights[i] = s[i] * (1.f / 32768.f); break;
+ default:
+ ERROR_LOG_REPORT_ONCE(fmtw0, Log::G3D, "Reader: Unsupported W0 Format %d", decFmt_.w0fmt);
+ memset(weights, 0, sizeof(float) * 8);
+ break;
+ }
+
+ f = (const float *)(data_ + decFmt_.w1off);
+ b = (const u8 *)(data_ + decFmt_.w1off);
+ s = (const u16 *)(data_ + decFmt_.w1off);
+ switch (decFmt_.w1fmt) {
+ case 0:
+ // It's fine for there to be w0 weights but not w1.
+ break;
+ case DEC_FLOAT_1:
+ case DEC_FLOAT_2:
+ case DEC_FLOAT_3:
+ case DEC_FLOAT_4:
+ for (int i = 0; i <= decFmt_.w1fmt - DEC_FLOAT_1; i++)
+ weights[i + 4] = f[i];
+ break;
+ case DEC_U8_1: weights[4] = b[0] * (1.f / 128.f); break;
+ case DEC_U8_2: for (int i = 0; i < 2; i++) weights[i + 4] = b[i] * (1.f / 128.f); break;
+ case DEC_U8_3: for (int i = 0; i < 3; i++) weights[i + 4] = b[i] * (1.f / 128.f); break;
+ case DEC_U8_4: for (int i = 0; i < 4; i++) weights[i + 4] = b[i] * (1.f / 128.f); break;
+ case DEC_U16_1: weights[4] = s[0] * (1.f / 32768.f); break;
+ case DEC_U16_2: for (int i = 0; i < 2; i++) weights[i + 4] = s[i] * (1.f / 32768.f); break;
+ case DEC_U16_3: for (int i = 0; i < 3; i++) weights[i + 4] = s[i] * (1.f / 32768.f); break;
+ case DEC_U16_4: for (int i = 0; i < 4; i++) weights[i + 4] = s[i] * (1.f / 32768.f); break;
+ default:
+ memset(weights + 4, 0, sizeof(float) * 4);
+ break;
+ }
+ }
+
+ bool hasColor0() const { return decFmt_.c0fmt != 0; }
+ bool hasColor1() const { return decFmt_.c1fmt != 0; }
+ bool hasNormal() const { return decFmt_.nrmfmt != 0; }
+ bool hasUV() const { return decFmt_.uvfmt != 0; }
+ bool isThrough() const { return (vtype_ & GE_VTYPE_THROUGH) != 0; }
+ void Goto(int index) {
+ data_ = base_ + index * decFmt_.stride;
+ }
+
+private:
+ const u8 *base_;
+ const u8 *data_;
+ DecVtxFormat decFmt_;
+ int vtype_;
+};
+// Debugging utilities
+void PrintDecodedVertex(const VertexReader &vtx);
diff --git a/GPU/GPU.vcxproj b/GPU/GPU.vcxproj
index 4c02d7f78a..5fd034eb66 100644
--- a/GPU/GPU.vcxproj
+++ b/GPU/GPU.vcxproj
@@ -281,6 +281,7 @@
+
diff --git a/GPU/GPU.vcxproj.filters b/GPU/GPU.vcxproj.filters
index e7e3d17f4a..b3b7a6a2de 100644
--- a/GPU/GPU.vcxproj.filters
+++ b/GPU/GPU.vcxproj.filters
@@ -264,6 +264,9 @@
Common
+
+ Common
+
diff --git a/GPU/Software/TransformUnit.cpp b/GPU/Software/TransformUnit.cpp
index 92a8a6c1e6..f57c1fcc02 100644
--- a/GPU/Software/TransformUnit.cpp
+++ b/GPU/Software/TransformUnit.cpp
@@ -28,6 +28,7 @@
#include "GPU/Common/DrawEngineCommon.h"
#include "GPU/Common/VertexDecoderCommon.h"
#include "GPU/Common/SoftwareTransformCommon.h"
+#include "GPU/Common/VertexReader.h"
#include "Common/Math/SIMDHeaders.h"
#include "GPU/Software/BinManager.h"
#include "GPU/Software/Clipper.h"