mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-02-08 13:43:33 +01:00
Merge pull request #10808 from Pokechu22/vertex-loader-direct-normals-with-index3
VideoCommon: Fix direct normal+tangent+binormal with index3 set
This commit is contained in:
commit
3b10bf04ac
@ -485,13 +485,22 @@ void ARM64XEmitter::EncodeLoadStorePairedInst(u32 op, ARM64Reg Rt, ARM64Reg Rt2,
|
|||||||
bool bVec = IsVector(Rt);
|
bool bVec = IsVector(Rt);
|
||||||
|
|
||||||
if (b128Bit)
|
if (b128Bit)
|
||||||
|
{
|
||||||
|
ASSERT_MSG(DYNA_REC, (imm & 0xf) == 0, "128-bit load/store must use aligned offset: {}", imm);
|
||||||
imm >>= 4;
|
imm >>= 4;
|
||||||
|
}
|
||||||
else if (b64Bit)
|
else if (b64Bit)
|
||||||
|
{
|
||||||
|
ASSERT_MSG(DYNA_REC, (imm & 0x7) == 0, "64-bit load/store must use aligned offset: {}", imm);
|
||||||
imm >>= 3;
|
imm >>= 3;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
ASSERT_MSG(DYNA_REC, (imm & 0x3) == 0, "32-bit load/store must use aligned offset: {}", imm);
|
||||||
imm >>= 2;
|
imm >>= 2;
|
||||||
|
}
|
||||||
|
|
||||||
ASSERT_MSG(DYNA_REC, !(imm & ~0xF), "offset too large {}", imm);
|
ASSERT_MSG(DYNA_REC, (imm & ~0xF) == 0, "offset too large {}", imm);
|
||||||
|
|
||||||
u32 opc = 0;
|
u32 opc = 0;
|
||||||
if (b128Bit)
|
if (b128Bit)
|
||||||
@ -524,11 +533,20 @@ void ARM64XEmitter::EncodeLoadStoreIndexedInst(u32 op, ARM64Reg Rt, ARM64Reg Rn,
|
|||||||
bool bVec = IsVector(Rt);
|
bool bVec = IsVector(Rt);
|
||||||
|
|
||||||
if (size == 64)
|
if (size == 64)
|
||||||
|
{
|
||||||
|
ASSERT_MSG(DYNA_REC, (imm & 0x7) == 0, "64-bit load/store must use aligned offset: {}", imm);
|
||||||
imm >>= 3;
|
imm >>= 3;
|
||||||
|
}
|
||||||
else if (size == 32)
|
else if (size == 32)
|
||||||
|
{
|
||||||
|
ASSERT_MSG(DYNA_REC, (imm & 0x3) == 0, "32-bit load/store must use aligned offset: {}", imm);
|
||||||
imm >>= 2;
|
imm >>= 2;
|
||||||
|
}
|
||||||
else if (size == 16)
|
else if (size == 16)
|
||||||
|
{
|
||||||
|
ASSERT_MSG(DYNA_REC, (imm & 0x1) == 0, "16-bit load/store must use aligned offset: {}", imm);
|
||||||
imm >>= 1;
|
imm >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
ASSERT_MSG(DYNA_REC, imm >= 0, "(IndexType::Unsigned): offset must be positive {}", imm);
|
ASSERT_MSG(DYNA_REC, imm >= 0, "(IndexType::Unsigned): offset must be positive {}", imm);
|
||||||
ASSERT_MSG(DYNA_REC, !(imm & ~0xFFF), "(IndexType::Unsigned): offset too large {}", imm);
|
ASSERT_MSG(DYNA_REC, !(imm & ~0xFFF), "(IndexType::Unsigned): offset too large {}", imm);
|
||||||
@ -615,10 +633,12 @@ void ARM64XEmitter::EncodeLoadStorePair(u32 op, u32 load, IndexType type, ARM64R
|
|||||||
if (b64Bit)
|
if (b64Bit)
|
||||||
{
|
{
|
||||||
op |= 0b10;
|
op |= 0b10;
|
||||||
|
ASSERT_MSG(DYNA_REC, (imm & 0x7) == 0, "64-bit load/store must use aligned offset: {}", imm);
|
||||||
imm >>= 3;
|
imm >>= 3;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
ASSERT_MSG(DYNA_REC, (imm & 0x3) == 0, "32-bit load/store must use aligned offset: {}", imm);
|
||||||
imm >>= 2;
|
imm >>= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2072,19 +2092,29 @@ void ARM64FloatEmitter::EmitLoadStoreImmediate(u8 size, u32 opc, IndexType type,
|
|||||||
|
|
||||||
if (type == IndexType::Unsigned)
|
if (type == IndexType::Unsigned)
|
||||||
{
|
{
|
||||||
ASSERT_MSG(DYNA_REC, !(imm & ((size - 1) >> 3)),
|
|
||||||
"(IndexType::Unsigned) immediate offset must be aligned to size! ({}) ({})", imm,
|
|
||||||
fmt::ptr(m_emit->GetCodePtr()));
|
|
||||||
ASSERT_MSG(DYNA_REC, imm >= 0, "(IndexType::Unsigned) immediate offset must be positive! ({})",
|
ASSERT_MSG(DYNA_REC, imm >= 0, "(IndexType::Unsigned) immediate offset must be positive! ({})",
|
||||||
imm);
|
imm);
|
||||||
if (size == 16)
|
if (size == 16)
|
||||||
|
{
|
||||||
|
ASSERT_MSG(DYNA_REC, (imm & 0x1) == 0, "16-bit load/store must use aligned offset: {}", imm);
|
||||||
imm >>= 1;
|
imm >>= 1;
|
||||||
|
}
|
||||||
else if (size == 32)
|
else if (size == 32)
|
||||||
|
{
|
||||||
|
ASSERT_MSG(DYNA_REC, (imm & 0x3) == 0, "32-bit load/store must use aligned offset: {}", imm);
|
||||||
imm >>= 2;
|
imm >>= 2;
|
||||||
|
}
|
||||||
else if (size == 64)
|
else if (size == 64)
|
||||||
|
{
|
||||||
|
ASSERT_MSG(DYNA_REC, (imm & 0x7) == 0, "64-bit load/store must use aligned offset: {}", imm);
|
||||||
imm >>= 3;
|
imm >>= 3;
|
||||||
|
}
|
||||||
else if (size == 128)
|
else if (size == 128)
|
||||||
|
{
|
||||||
|
ASSERT_MSG(DYNA_REC, (imm & 0xf) == 0, "128-bit load/store must use aligned offset: {}", imm);
|
||||||
imm >>= 4;
|
imm >>= 4;
|
||||||
|
}
|
||||||
|
ASSERT_MSG(DYNA_REC, imm <= 0xFFF, "Immediate value is too big: {}", imm);
|
||||||
encoded_imm = (imm & 0xFFF);
|
encoded_imm = (imm & 0xFFF);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -60,38 +60,22 @@ VertexLoaderARM64::VertexLoaderARM64(const TVtxDesc& vtx_desc, const VAT& vtx_at
|
|||||||
WriteProtect();
|
WriteProtect();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VertexLoaderARM64::GetVertexAddr(CPArray array, VertexComponentFormat attribute, ARM64Reg reg)
|
// Returns the register to use as the base and an offset from that register.
|
||||||
|
// For indexed attributes, the index is read into scratch1_reg, and then scratch1_reg with no offset
|
||||||
|
// is returned. For direct attributes, an offset from src_reg is returned.
|
||||||
|
std::pair<Arm64Gen::ARM64Reg, u32> VertexLoaderARM64::GetVertexAddr(CPArray array,
|
||||||
|
VertexComponentFormat attribute)
|
||||||
{
|
{
|
||||||
if (IsIndexed(attribute))
|
if (IsIndexed(attribute))
|
||||||
{
|
{
|
||||||
if (attribute == VertexComponentFormat::Index8)
|
if (attribute == VertexComponentFormat::Index8)
|
||||||
{
|
{
|
||||||
if (m_src_ofs < 4096)
|
LDURB(scratch1_reg, src_reg, m_src_ofs);
|
||||||
{
|
|
||||||
LDRB(IndexType::Unsigned, scratch1_reg, src_reg, m_src_ofs);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ADD(reg, src_reg, m_src_ofs);
|
|
||||||
LDRB(IndexType::Unsigned, scratch1_reg, reg, 0);
|
|
||||||
}
|
|
||||||
m_src_ofs += 1;
|
m_src_ofs += 1;
|
||||||
}
|
}
|
||||||
else
|
else // Index16
|
||||||
{
|
|
||||||
if (m_src_ofs < 256)
|
|
||||||
{
|
{
|
||||||
LDURH(scratch1_reg, src_reg, m_src_ofs);
|
LDURH(scratch1_reg, src_reg, m_src_ofs);
|
||||||
}
|
|
||||||
else if (m_src_ofs <= 8190 && !(m_src_ofs & 1))
|
|
||||||
{
|
|
||||||
LDRH(IndexType::Unsigned, scratch1_reg, src_reg, m_src_ofs);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ADD(reg, src_reg, m_src_ofs);
|
|
||||||
LDRH(IndexType::Unsigned, scratch1_reg, reg, 0);
|
|
||||||
}
|
|
||||||
m_src_ofs += 2;
|
m_src_ofs += 2;
|
||||||
REV16(scratch1_reg, scratch1_reg);
|
REV16(scratch1_reg, scratch1_reg);
|
||||||
}
|
}
|
||||||
@ -109,25 +93,19 @@ void VertexLoaderARM64::GetVertexAddr(CPArray array, VertexComponentFormat attri
|
|||||||
|
|
||||||
LDR(IndexType::Unsigned, EncodeRegTo64(scratch2_reg), arraybase_reg,
|
LDR(IndexType::Unsigned, EncodeRegTo64(scratch2_reg), arraybase_reg,
|
||||||
static_cast<u8>(array) * 8);
|
static_cast<u8>(array) * 8);
|
||||||
ADD(EncodeRegTo64(reg), EncodeRegTo64(scratch1_reg), EncodeRegTo64(scratch2_reg));
|
ADD(EncodeRegTo64(scratch1_reg), EncodeRegTo64(scratch1_reg), EncodeRegTo64(scratch2_reg));
|
||||||
|
return {EncodeRegTo64(scratch1_reg), 0};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
ADD(reg, src_reg, m_src_ofs);
|
{
|
||||||
|
return {src_reg, m_src_ofs};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 VertexLoaderARM64::GetAddressImm(CPArray array, VertexComponentFormat attribute,
|
void VertexLoaderARM64::ReadVertex(VertexComponentFormat attribute, ComponentFormat format,
|
||||||
Arm64Gen::ARM64Reg reg, u32 align)
|
int count_in, int count_out, bool dequantize,
|
||||||
{
|
u8 scaling_exponent, AttributeFormat* native_format,
|
||||||
if (IsIndexed(attribute) || (m_src_ofs > 255 && (m_src_ofs & (align - 1))))
|
ARM64Reg reg, u32 offset)
|
||||||
GetVertexAddr(array, attribute, reg);
|
|
||||||
else
|
|
||||||
return m_src_ofs;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int VertexLoaderARM64::ReadVertex(VertexComponentFormat attribute, ComponentFormat format,
|
|
||||||
int count_in, int count_out, bool dequantize, u8 scaling_exponent,
|
|
||||||
AttributeFormat* native_format, s32 offset)
|
|
||||||
{
|
{
|
||||||
ARM64Reg coords = count_in == 3 ? ARM64Reg::Q31 : ARM64Reg::D31;
|
ARM64Reg coords = count_in == 3 ? ARM64Reg::Q31 : ARM64Reg::D31;
|
||||||
ARM64Reg scale = count_in == 3 ? ARM64Reg::Q30 : ARM64Reg::D30;
|
ARM64Reg scale = count_in == 3 ? ARM64Reg::Q30 : ARM64Reg::D30;
|
||||||
@ -136,23 +114,8 @@ int VertexLoaderARM64::ReadVertex(VertexComponentFormat attribute, ComponentForm
|
|||||||
int load_bytes = elem_size * count_in;
|
int load_bytes = elem_size * count_in;
|
||||||
int load_size = GetLoadSize(load_bytes);
|
int load_size = GetLoadSize(load_bytes);
|
||||||
load_size <<= 3;
|
load_size <<= 3;
|
||||||
elem_size <<= 3;
|
|
||||||
|
|
||||||
if (offset == -1)
|
m_float_emit.LDUR(load_size, coords, reg, offset);
|
||||||
{
|
|
||||||
if (count_in == 1)
|
|
||||||
m_float_emit.LDR(elem_size, IndexType::Unsigned, coords, EncodeRegTo64(scratch1_reg), 0);
|
|
||||||
else
|
|
||||||
m_float_emit.LD1(elem_size, 1, coords, EncodeRegTo64(scratch1_reg));
|
|
||||||
}
|
|
||||||
else if (offset & (load_size - 1)) // Not aligned - unscaled
|
|
||||||
{
|
|
||||||
m_float_emit.LDUR(load_size, coords, src_reg, offset);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_float_emit.LDR(load_size, IndexType::Unsigned, coords, src_reg, offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (format != ComponentFormat::Float)
|
if (format != ComponentFormat::Float)
|
||||||
{
|
{
|
||||||
@ -191,20 +154,7 @@ int VertexLoaderARM64::ReadVertex(VertexComponentFormat attribute, ComponentForm
|
|||||||
}
|
}
|
||||||
|
|
||||||
const u32 write_size = count_out == 3 ? 128 : count_out * 32;
|
const u32 write_size = count_out == 3 ? 128 : count_out * 32;
|
||||||
const u32 mask = count_out == 3 ? 0xF : count_out == 2 ? 0x7 : 0x3;
|
|
||||||
if (m_dst_ofs < 256)
|
|
||||||
{
|
|
||||||
m_float_emit.STUR(write_size, coords, dst_reg, m_dst_ofs);
|
m_float_emit.STUR(write_size, coords, dst_reg, m_dst_ofs);
|
||||||
}
|
|
||||||
else if (!(m_dst_ofs & mask))
|
|
||||||
{
|
|
||||||
m_float_emit.STR(write_size, IndexType::Unsigned, coords, dst_reg, m_dst_ofs);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ADD(EncodeRegTo64(scratch2_reg), dst_reg, m_dst_ofs);
|
|
||||||
m_float_emit.ST1(32, 1, coords, EncodeRegTo64(scratch2_reg));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Z-Freeze
|
// Z-Freeze
|
||||||
if (native_format == &m_native_vtx_decl.position)
|
if (native_format == &m_native_vtx_decl.position)
|
||||||
@ -239,11 +189,10 @@ int VertexLoaderARM64::ReadVertex(VertexComponentFormat attribute, ComponentForm
|
|||||||
|
|
||||||
if (attribute == VertexComponentFormat::Direct)
|
if (attribute == VertexComponentFormat::Direct)
|
||||||
m_src_ofs += load_bytes;
|
m_src_ofs += load_bytes;
|
||||||
|
|
||||||
return load_bytes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VertexLoaderARM64::ReadColor(VertexComponentFormat attribute, ColorFormat format, s32 offset)
|
void VertexLoaderARM64::ReadColor(VertexComponentFormat attribute, ColorFormat format, ARM64Reg reg,
|
||||||
|
u32 offset)
|
||||||
{
|
{
|
||||||
int load_bytes = 0;
|
int load_bytes = 0;
|
||||||
switch (format)
|
switch (format)
|
||||||
@ -251,12 +200,7 @@ void VertexLoaderARM64::ReadColor(VertexComponentFormat attribute, ColorFormat f
|
|||||||
case ColorFormat::RGB888:
|
case ColorFormat::RGB888:
|
||||||
case ColorFormat::RGB888x:
|
case ColorFormat::RGB888x:
|
||||||
case ColorFormat::RGBA8888:
|
case ColorFormat::RGBA8888:
|
||||||
if (offset == -1)
|
LDUR(scratch2_reg, reg, offset);
|
||||||
LDR(IndexType::Unsigned, scratch2_reg, EncodeRegTo64(scratch1_reg), 0);
|
|
||||||
else if (offset & 3) // Not aligned - unscaled
|
|
||||||
LDUR(scratch2_reg, src_reg, offset);
|
|
||||||
else
|
|
||||||
LDR(IndexType::Unsigned, scratch2_reg, src_reg, offset);
|
|
||||||
|
|
||||||
if (format != ColorFormat::RGBA8888)
|
if (format != ColorFormat::RGBA8888)
|
||||||
ORR(scratch2_reg, scratch2_reg, LogicalImm(0xFF000000, 32));
|
ORR(scratch2_reg, scratch2_reg, LogicalImm(0xFF000000, 32));
|
||||||
@ -267,12 +211,7 @@ void VertexLoaderARM64::ReadColor(VertexComponentFormat attribute, ColorFormat f
|
|||||||
case ColorFormat::RGB565:
|
case ColorFormat::RGB565:
|
||||||
// RRRRRGGG GGGBBBBB
|
// RRRRRGGG GGGBBBBB
|
||||||
// AAAAAAAA BBBBBBBB GGGGGGGG RRRRRRRR
|
// AAAAAAAA BBBBBBBB GGGGGGGG RRRRRRRR
|
||||||
if (offset == -1)
|
LDURH(scratch3_reg, reg, offset);
|
||||||
LDRH(IndexType::Unsigned, scratch3_reg, EncodeRegTo64(scratch1_reg), 0);
|
|
||||||
else if (offset & 1) // Not aligned - unscaled
|
|
||||||
LDURH(scratch3_reg, src_reg, offset);
|
|
||||||
else
|
|
||||||
LDRH(IndexType::Unsigned, scratch3_reg, src_reg, offset);
|
|
||||||
|
|
||||||
REV16(scratch3_reg, scratch3_reg);
|
REV16(scratch3_reg, scratch3_reg);
|
||||||
|
|
||||||
@ -304,12 +243,7 @@ void VertexLoaderARM64::ReadColor(VertexComponentFormat attribute, ColorFormat f
|
|||||||
// BBBBAAAA RRRRGGGG
|
// BBBBAAAA RRRRGGGG
|
||||||
// REV16 - RRRRGGGG BBBBAAAA
|
// REV16 - RRRRGGGG BBBBAAAA
|
||||||
// AAAAAAAA BBBBBBBB GGGGGGGG RRRRRRRR
|
// AAAAAAAA BBBBBBBB GGGGGGGG RRRRRRRR
|
||||||
if (offset == -1)
|
LDURH(scratch3_reg, reg, offset);
|
||||||
LDRH(IndexType::Unsigned, scratch3_reg, EncodeRegTo64(scratch1_reg), 0);
|
|
||||||
else if (offset & 1) // Not aligned - unscaled
|
|
||||||
LDURH(scratch3_reg, src_reg, offset);
|
|
||||||
else
|
|
||||||
LDRH(IndexType::Unsigned, scratch3_reg, src_reg, offset);
|
|
||||||
|
|
||||||
// R
|
// R
|
||||||
UBFM(scratch1_reg, scratch3_reg, 4, 7);
|
UBFM(scratch1_reg, scratch3_reg, 4, 7);
|
||||||
@ -336,18 +270,7 @@ void VertexLoaderARM64::ReadColor(VertexComponentFormat attribute, ColorFormat f
|
|||||||
case ColorFormat::RGBA6666:
|
case ColorFormat::RGBA6666:
|
||||||
// RRRRRRGG GGGGBBBB BBAAAAAA
|
// RRRRRRGG GGGGBBBB BBAAAAAA
|
||||||
// AAAAAAAA BBBBBBBB GGGGGGGG RRRRRRRR
|
// AAAAAAAA BBBBBBBB GGGGGGGG RRRRRRRR
|
||||||
if (offset == -1)
|
LDUR(scratch3_reg, reg, offset - 1);
|
||||||
{
|
|
||||||
LDUR(scratch3_reg, EncodeRegTo64(scratch1_reg), -1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
offset -= 1;
|
|
||||||
if (offset & 3) // Not aligned - unscaled
|
|
||||||
LDUR(scratch3_reg, src_reg, offset);
|
|
||||||
else
|
|
||||||
LDR(IndexType::Unsigned, scratch3_reg, src_reg, offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
REV32(scratch3_reg, scratch3_reg);
|
REV32(scratch3_reg, scratch3_reg);
|
||||||
|
|
||||||
@ -385,6 +308,12 @@ void VertexLoaderARM64::ReadColor(VertexComponentFormat attribute, ColorFormat f
|
|||||||
|
|
||||||
void VertexLoaderARM64::GenerateVertexLoader()
|
void VertexLoaderARM64::GenerateVertexLoader()
|
||||||
{
|
{
|
||||||
|
// The largest input vertex (with the position matrix index and all texture matrix indices
|
||||||
|
// enabled, and all components set as direct) is 129 bytes (corresponding to a 156-byte
|
||||||
|
// output). This is small enough that we can always use the unscaled load/store instructions
|
||||||
|
// (which allow an offset from -256 to +255).
|
||||||
|
ASSERT(m_vertex_size <= 255);
|
||||||
|
|
||||||
// R0 - Source pointer
|
// R0 - Source pointer
|
||||||
// R1 - Destination pointer
|
// R1 - Destination pointer
|
||||||
// R2 - Count
|
// R2 - Count
|
||||||
@ -456,49 +385,45 @@ void VertexLoaderARM64::GenerateVertexLoader()
|
|||||||
|
|
||||||
// Position
|
// Position
|
||||||
{
|
{
|
||||||
int elem_size = GetElementSize(m_VtxAttr.g0.PosFormat);
|
const int pos_elements = m_VtxAttr.g0.PosElements == CoordComponentCount::XY ? 2 : 3;
|
||||||
int pos_elements = m_VtxAttr.g0.PosElements == CoordComponentCount::XY ? 2 : 3;
|
|
||||||
int load_bytes = elem_size * pos_elements;
|
|
||||||
int load_size = GetLoadSize(load_bytes);
|
|
||||||
load_size <<= 3;
|
|
||||||
|
|
||||||
s32 offset = GetAddressImm(CPArray::Position, m_VtxDesc.low.Position,
|
const auto [reg, offset] = GetVertexAddr(CPArray::Position, m_VtxDesc.low.Position);
|
||||||
EncodeRegTo64(scratch1_reg), load_size);
|
|
||||||
ReadVertex(m_VtxDesc.low.Position, m_VtxAttr.g0.PosFormat, pos_elements, pos_elements,
|
ReadVertex(m_VtxDesc.low.Position, m_VtxAttr.g0.PosFormat, pos_elements, pos_elements,
|
||||||
m_VtxAttr.g0.ByteDequant, m_VtxAttr.g0.PosFrac, &m_native_vtx_decl.position, offset);
|
m_VtxAttr.g0.ByteDequant, m_VtxAttr.g0.PosFrac, &m_native_vtx_decl.position, reg,
|
||||||
|
offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_VtxDesc.low.Normal != VertexComponentFormat::NotPresent)
|
if (m_VtxDesc.low.Normal != VertexComponentFormat::NotPresent)
|
||||||
{
|
{
|
||||||
static const u8 map[8] = {7, 6, 15, 14};
|
static constexpr Common::EnumMap<u8, static_cast<ComponentFormat>(7)> SCALE_MAP = {7, 6, 15, 14,
|
||||||
const u8 scaling_exponent = map[u32(m_VtxAttr.g0.NormalFormat.Value())];
|
0, 0, 0, 0};
|
||||||
const int limit = m_VtxAttr.g0.NormalElements == NormalComponentCount::NTB ? 3 : 1;
|
const u8 scaling_exponent = SCALE_MAP[m_VtxAttr.g0.NormalFormat];
|
||||||
|
|
||||||
s32 offset = -1;
|
// Normal
|
||||||
for (int i = 0; i < limit; i++)
|
auto [reg, offset] = GetVertexAddr(CPArray::Normal, m_VtxDesc.low.Normal);
|
||||||
|
ReadVertex(m_VtxDesc.low.Normal, m_VtxAttr.g0.NormalFormat, 3, 3, true, scaling_exponent,
|
||||||
|
&m_native_vtx_decl.normals[0], reg, offset);
|
||||||
|
|
||||||
|
if (m_VtxAttr.g0.NormalElements == NormalComponentCount::NTB)
|
||||||
{
|
{
|
||||||
if (!i || m_VtxAttr.g0.NormalIndex3)
|
const bool index3 = IsIndexed(m_VtxDesc.low.Normal) && m_VtxAttr.g0.NormalIndex3;
|
||||||
{
|
const int elem_size = GetElementSize(m_VtxAttr.g0.NormalFormat);
|
||||||
int elem_size = GetElementSize(m_VtxAttr.g0.NormalFormat);
|
const int load_bytes = elem_size * 3;
|
||||||
|
|
||||||
int load_bytes = elem_size * 3;
|
// Tangent
|
||||||
int load_size = GetLoadSize(load_bytes);
|
// If in Index3 mode, and indexed components are used, replace the index with a new index.
|
||||||
|
if (index3)
|
||||||
offset = GetAddressImm(CPArray::Normal, m_VtxDesc.low.Normal, EncodeRegTo64(scratch1_reg),
|
std::tie(reg, offset) = GetVertexAddr(CPArray::Normal, m_VtxDesc.low.Normal);
|
||||||
load_size << 3);
|
// The tangent comes after the normal; even in index3 mode, an extra offset of load_bytes is
|
||||||
|
// applied. Note that this is different from adding 1 to the index, as the stride for indices
|
||||||
if (offset == -1)
|
// may be different from the size of the tangent itself.
|
||||||
ADD(EncodeRegTo64(scratch1_reg), EncodeRegTo64(scratch1_reg), i * elem_size * 3);
|
ReadVertex(m_VtxDesc.low.Normal, m_VtxAttr.g0.NormalFormat, 3, 3, true, scaling_exponent,
|
||||||
else
|
&m_native_vtx_decl.normals[1], reg, offset + load_bytes);
|
||||||
offset += i * elem_size * 3;
|
// Binormal
|
||||||
}
|
if (index3)
|
||||||
int bytes_read = ReadVertex(m_VtxDesc.low.Normal, m_VtxAttr.g0.NormalFormat, 3, 3, true,
|
std::tie(reg, offset) = GetVertexAddr(CPArray::Normal, m_VtxDesc.low.Normal);
|
||||||
scaling_exponent, &m_native_vtx_decl.normals[i], offset);
|
ReadVertex(m_VtxDesc.low.Normal, m_VtxAttr.g0.NormalFormat, 3, 3, true, scaling_exponent,
|
||||||
|
&m_native_vtx_decl.normals[2], reg, offset + load_bytes * 2);
|
||||||
if (offset == -1)
|
|
||||||
ADD(EncodeRegTo64(scratch1_reg), EncodeRegTo64(scratch1_reg), bytes_read);
|
|
||||||
else
|
|
||||||
offset += bytes_read;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -510,14 +435,8 @@ void VertexLoaderARM64::GenerateVertexLoader()
|
|||||||
|
|
||||||
if (m_VtxDesc.low.Color[i] != VertexComponentFormat::NotPresent)
|
if (m_VtxDesc.low.Color[i] != VertexComponentFormat::NotPresent)
|
||||||
{
|
{
|
||||||
u32 align = 4;
|
const auto [reg, offset] = GetVertexAddr(CPArray::Color0 + i, m_VtxDesc.low.Color[i]);
|
||||||
if (m_VtxAttr.GetColorFormat(i) == ColorFormat::RGB565 ||
|
ReadColor(m_VtxDesc.low.Color[i], m_VtxAttr.GetColorFormat(i), reg, offset);
|
||||||
m_VtxAttr.GetColorFormat(i) == ColorFormat::RGBA4444)
|
|
||||||
align = 2;
|
|
||||||
|
|
||||||
s32 offset = GetAddressImm(CPArray::Color0 + i, m_VtxDesc.low.Color[i],
|
|
||||||
EncodeRegTo64(scratch1_reg), align);
|
|
||||||
ReadColor(m_VtxDesc.low.Color[i], m_VtxAttr.GetColorFormat(i), offset);
|
|
||||||
m_native_vtx_decl.colors[i].components = 4;
|
m_native_vtx_decl.colors[i].components = 4;
|
||||||
m_native_vtx_decl.colors[i].enable = true;
|
m_native_vtx_decl.colors[i].enable = true;
|
||||||
m_native_vtx_decl.colors[i].offset = m_dst_ofs;
|
m_native_vtx_decl.colors[i].offset = m_dst_ofs;
|
||||||
@ -533,20 +452,14 @@ void VertexLoaderARM64::GenerateVertexLoader()
|
|||||||
m_native_vtx_decl.texcoords[i].type = ComponentFormat::Float;
|
m_native_vtx_decl.texcoords[i].type = ComponentFormat::Float;
|
||||||
m_native_vtx_decl.texcoords[i].integer = false;
|
m_native_vtx_decl.texcoords[i].integer = false;
|
||||||
|
|
||||||
int elements = m_VtxAttr.GetTexElements(i) == TexComponentCount::S ? 1 : 2;
|
const int elements = m_VtxAttr.GetTexElements(i) == TexComponentCount::S ? 1 : 2;
|
||||||
if (m_VtxDesc.high.TexCoord[i] != VertexComponentFormat::NotPresent)
|
if (m_VtxDesc.high.TexCoord[i] != VertexComponentFormat::NotPresent)
|
||||||
{
|
{
|
||||||
int elem_size = GetElementSize(m_VtxAttr.GetTexFormat(i));
|
const auto [reg, offset] = GetVertexAddr(CPArray::TexCoord0 + i, m_VtxDesc.high.TexCoord[i]);
|
||||||
int load_bytes = elem_size * (elements + 2);
|
|
||||||
int load_size = GetLoadSize(load_bytes);
|
|
||||||
load_size <<= 3;
|
|
||||||
|
|
||||||
s32 offset = GetAddressImm(CPArray::TexCoord0 + i, m_VtxDesc.high.TexCoord[i],
|
|
||||||
EncodeRegTo64(scratch1_reg), load_size);
|
|
||||||
u8 scaling_exponent = m_VtxAttr.GetTexFrac(i);
|
u8 scaling_exponent = m_VtxAttr.GetTexFrac(i);
|
||||||
ReadVertex(m_VtxDesc.high.TexCoord[i], m_VtxAttr.GetTexFormat(i), elements,
|
ReadVertex(m_VtxDesc.high.TexCoord[i], m_VtxAttr.GetTexFormat(i), elements,
|
||||||
m_VtxDesc.low.TexMatIdx[i] ? 2 : elements, m_VtxAttr.g0.ByteDequant,
|
m_VtxDesc.low.TexMatIdx[i] ? 2 : elements, m_VtxAttr.g0.ByteDequant,
|
||||||
scaling_exponent, &m_native_vtx_decl.texcoords[i], offset);
|
scaling_exponent, &m_native_vtx_decl.texcoords[i], reg, offset);
|
||||||
}
|
}
|
||||||
if (m_VtxDesc.low.TexMatIdx[i])
|
if (m_VtxDesc.low.TexMatIdx[i])
|
||||||
{
|
{
|
||||||
@ -567,22 +480,7 @@ void VertexLoaderARM64::GenerateVertexLoader()
|
|||||||
{
|
{
|
||||||
m_native_vtx_decl.texcoords[i].offset = m_dst_ofs;
|
m_native_vtx_decl.texcoords[i].offset = m_dst_ofs;
|
||||||
|
|
||||||
if (m_dst_ofs < 256)
|
|
||||||
{
|
|
||||||
STUR(ARM64Reg::SP, dst_reg, m_dst_ofs);
|
STUR(ARM64Reg::SP, dst_reg, m_dst_ofs);
|
||||||
}
|
|
||||||
else if (!(m_dst_ofs & 7))
|
|
||||||
{
|
|
||||||
// If m_dst_ofs isn't 8byte aligned we can't store an 8byte zero register
|
|
||||||
// So store two 4byte zero registers
|
|
||||||
// The destination is always 4byte aligned
|
|
||||||
STR(IndexType::Unsigned, ARM64Reg::WSP, dst_reg, m_dst_ofs);
|
|
||||||
STR(IndexType::Unsigned, ARM64Reg::WSP, dst_reg, m_dst_ofs + 4);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
STR(IndexType::Unsigned, ARM64Reg::SP, dst_reg, m_dst_ofs);
|
|
||||||
}
|
|
||||||
m_float_emit.STR(32, IndexType::Unsigned, ARM64Reg::D31, dst_reg, m_dst_ofs + 8);
|
m_float_emit.STR(32, IndexType::Unsigned, ARM64Reg::D31, dst_reg, m_dst_ofs + 8);
|
||||||
|
|
||||||
m_dst_ofs += sizeof(float) * 3;
|
m_dst_ofs += sizeof(float) * 3;
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "Common/Arm64Emitter.h"
|
#include "Common/Arm64Emitter.h"
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
#include "VideoCommon/VertexLoaderBase.h"
|
#include "VideoCommon/VertexLoaderBase.h"
|
||||||
@ -26,12 +28,11 @@ private:
|
|||||||
u32 m_dst_ofs = 0;
|
u32 m_dst_ofs = 0;
|
||||||
Arm64Gen::FixupBranch m_skip_vertex;
|
Arm64Gen::FixupBranch m_skip_vertex;
|
||||||
Arm64Gen::ARM64FloatEmitter m_float_emit;
|
Arm64Gen::ARM64FloatEmitter m_float_emit;
|
||||||
void GetVertexAddr(CPArray array, VertexComponentFormat attribute, Arm64Gen::ARM64Reg reg);
|
std::pair<Arm64Gen::ARM64Reg, u32> GetVertexAddr(CPArray array, VertexComponentFormat attribute);
|
||||||
s32 GetAddressImm(CPArray array, VertexComponentFormat attribute, Arm64Gen::ARM64Reg reg,
|
void ReadVertex(VertexComponentFormat attribute, ComponentFormat format, int count_in,
|
||||||
u32 align);
|
|
||||||
int ReadVertex(VertexComponentFormat attribute, ComponentFormat format, int count_in,
|
|
||||||
int count_out, bool dequantize, u8 scaling_exponent,
|
int count_out, bool dequantize, u8 scaling_exponent,
|
||||||
AttributeFormat* native_format, s32 offset = -1);
|
AttributeFormat* native_format, Arm64Gen::ARM64Reg reg, u32 offset);
|
||||||
void ReadColor(VertexComponentFormat attribute, ColorFormat format, s32 offset);
|
void ReadColor(VertexComponentFormat attribute, ColorFormat format, Arm64Gen::ARM64Reg reg,
|
||||||
|
u32 offset);
|
||||||
void GenerateVertexLoader();
|
void GenerateVertexLoader();
|
||||||
};
|
};
|
||||||
|
@ -79,8 +79,9 @@ OpArg VertexLoaderX64::GetVertexAddr(CPArray array, VertexComponentFormat attrib
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int VertexLoaderX64::ReadVertex(OpArg data, VertexComponentFormat attribute, ComponentFormat format,
|
void VertexLoaderX64::ReadVertex(OpArg data, VertexComponentFormat attribute,
|
||||||
int count_in, int count_out, bool dequantize, u8 scaling_exponent,
|
ComponentFormat format, int count_in, int count_out,
|
||||||
|
bool dequantize, u8 scaling_exponent,
|
||||||
AttributeFormat* native_format)
|
AttributeFormat* native_format)
|
||||||
{
|
{
|
||||||
static const __m128i shuffle_lut[5][3] = {
|
static const __m128i shuffle_lut[5][3] = {
|
||||||
@ -276,8 +277,6 @@ int VertexLoaderX64::ReadVertex(OpArg data, VertexComponentFormat attribute, Com
|
|||||||
}
|
}
|
||||||
|
|
||||||
write_zfreeze();
|
write_zfreeze();
|
||||||
|
|
||||||
return load_bytes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VertexLoaderX64::ReadColor(OpArg data, VertexComponentFormat attribute, ColorFormat format)
|
void VertexLoaderX64::ReadColor(OpArg data, VertexComponentFormat attribute, ColorFormat format)
|
||||||
@ -459,20 +458,45 @@ void VertexLoaderX64::GenerateVertexLoader()
|
|||||||
|
|
||||||
if (m_VtxDesc.low.Normal != VertexComponentFormat::NotPresent)
|
if (m_VtxDesc.low.Normal != VertexComponentFormat::NotPresent)
|
||||||
{
|
{
|
||||||
static const u8 map[8] = {7, 6, 15, 14};
|
static constexpr Common::EnumMap<u8, static_cast<ComponentFormat>(7)> SCALE_MAP = {7, 6, 15, 14,
|
||||||
const u8 scaling_exponent = map[u32(m_VtxAttr.g0.NormalFormat.Value())];
|
0, 0, 0, 0};
|
||||||
const int limit = m_VtxAttr.g0.NormalElements == NormalComponentCount::NTB ? 3 : 1;
|
const u8 scaling_exponent = SCALE_MAP[m_VtxAttr.g0.NormalFormat];
|
||||||
|
|
||||||
for (int i = 0; i < limit; i++)
|
// Normal
|
||||||
{
|
|
||||||
if (!i || m_VtxAttr.g0.NormalIndex3)
|
|
||||||
{
|
|
||||||
data = GetVertexAddr(CPArray::Normal, m_VtxDesc.low.Normal);
|
data = GetVertexAddr(CPArray::Normal, m_VtxDesc.low.Normal);
|
||||||
int elem_size = GetElementSize(m_VtxAttr.g0.NormalFormat);
|
ReadVertex(data, m_VtxDesc.low.Normal, m_VtxAttr.g0.NormalFormat, 3, 3, true, scaling_exponent,
|
||||||
data.AddMemOffset(i * elem_size * 3);
|
&m_native_vtx_decl.normals[0]);
|
||||||
}
|
|
||||||
data.AddMemOffset(ReadVertex(data, m_VtxDesc.low.Normal, m_VtxAttr.g0.NormalFormat, 3, 3,
|
if (m_VtxAttr.g0.NormalElements == NormalComponentCount::NTB)
|
||||||
true, scaling_exponent, &m_native_vtx_decl.normals[i]));
|
{
|
||||||
|
const bool index3 = IsIndexed(m_VtxDesc.low.Normal) && m_VtxAttr.g0.NormalIndex3;
|
||||||
|
const int elem_size = GetElementSize(m_VtxAttr.g0.NormalFormat);
|
||||||
|
const int load_bytes = elem_size * 3;
|
||||||
|
|
||||||
|
// Tangent
|
||||||
|
// If in Index3 mode, and indexed components are used, replace the index with a new index.
|
||||||
|
if (index3)
|
||||||
|
data = GetVertexAddr(CPArray::Normal, m_VtxDesc.low.Normal);
|
||||||
|
// The tangent comes after the normal; even in index3 mode, this offset is applied.
|
||||||
|
// Note that this is different from adding 1 to the index, as the stride for indices may be
|
||||||
|
// different from the size of the tangent itself.
|
||||||
|
data.AddMemOffset(load_bytes);
|
||||||
|
|
||||||
|
ReadVertex(data, m_VtxDesc.low.Normal, m_VtxAttr.g0.NormalFormat, 3, 3, true,
|
||||||
|
scaling_exponent, &m_native_vtx_decl.normals[1]);
|
||||||
|
|
||||||
|
// Undo the offset above so that data points to the normal instead of the tangent.
|
||||||
|
// This way, we can add 2*elem_size below to always point to the binormal, even if we replace
|
||||||
|
// data with a new index (which would point to the normal).
|
||||||
|
data.AddMemOffset(-load_bytes);
|
||||||
|
|
||||||
|
// Binormal
|
||||||
|
if (index3)
|
||||||
|
data = GetVertexAddr(CPArray::Normal, m_VtxDesc.low.Normal);
|
||||||
|
data.AddMemOffset(load_bytes * 2);
|
||||||
|
|
||||||
|
ReadVertex(data, m_VtxDesc.low.Normal, m_VtxAttr.g0.NormalFormat, 3, 3, true,
|
||||||
|
scaling_exponent, &m_native_vtx_decl.normals[2]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ private:
|
|||||||
u32 m_dst_ofs = 0;
|
u32 m_dst_ofs = 0;
|
||||||
Gen::FixupBranch m_skip_vertex;
|
Gen::FixupBranch m_skip_vertex;
|
||||||
Gen::OpArg GetVertexAddr(CPArray array, VertexComponentFormat attribute);
|
Gen::OpArg GetVertexAddr(CPArray array, VertexComponentFormat attribute);
|
||||||
int ReadVertex(Gen::OpArg data, VertexComponentFormat attribute, ComponentFormat format,
|
void ReadVertex(Gen::OpArg data, VertexComponentFormat attribute, ComponentFormat format,
|
||||||
int count_in, int count_out, bool dequantize, u8 scaling_exponent,
|
int count_in, int count_out, bool dequantize, u8 scaling_exponent,
|
||||||
AttributeFormat* native_format);
|
AttributeFormat* native_format);
|
||||||
void ReadColor(Gen::OpArg data, VertexComponentFormat attribute, ColorFormat format);
|
void ReadColor(Gen::OpArg data, VertexComponentFormat attribute, ColorFormat format);
|
||||||
|
@ -366,3 +366,413 @@ TEST_F(VertexLoaderTest, LargeFloatVertexSpeed)
|
|||||||
for (int i = 0; i < 100; ++i)
|
for (int i = 0; i < 100; ++i)
|
||||||
RunVertices(100000);
|
RunVertices(100000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(VertexLoaderTest, DirectAllComponents)
|
||||||
|
{
|
||||||
|
m_vtx_desc.low.PosMatIdx = 1;
|
||||||
|
m_vtx_desc.low.Tex0MatIdx = 1;
|
||||||
|
m_vtx_desc.low.Tex1MatIdx = 1;
|
||||||
|
m_vtx_desc.low.Tex2MatIdx = 1;
|
||||||
|
m_vtx_desc.low.Tex3MatIdx = 1;
|
||||||
|
m_vtx_desc.low.Tex4MatIdx = 1;
|
||||||
|
m_vtx_desc.low.Tex5MatIdx = 1;
|
||||||
|
m_vtx_desc.low.Tex6MatIdx = 1;
|
||||||
|
m_vtx_desc.low.Tex7MatIdx = 1;
|
||||||
|
m_vtx_desc.low.Position = VertexComponentFormat::Direct;
|
||||||
|
m_vtx_desc.low.Normal = VertexComponentFormat::Direct;
|
||||||
|
m_vtx_desc.low.Color0 = VertexComponentFormat::Direct;
|
||||||
|
m_vtx_desc.low.Color1 = VertexComponentFormat::Direct;
|
||||||
|
m_vtx_desc.high.Tex0Coord = VertexComponentFormat::Direct;
|
||||||
|
m_vtx_desc.high.Tex1Coord = VertexComponentFormat::Direct;
|
||||||
|
m_vtx_desc.high.Tex2Coord = VertexComponentFormat::Direct;
|
||||||
|
m_vtx_desc.high.Tex3Coord = VertexComponentFormat::Direct;
|
||||||
|
m_vtx_desc.high.Tex4Coord = VertexComponentFormat::Direct;
|
||||||
|
m_vtx_desc.high.Tex5Coord = VertexComponentFormat::Direct;
|
||||||
|
m_vtx_desc.high.Tex6Coord = VertexComponentFormat::Direct;
|
||||||
|
m_vtx_desc.high.Tex7Coord = VertexComponentFormat::Direct;
|
||||||
|
|
||||||
|
m_vtx_attr.g0.PosElements = CoordComponentCount::XYZ;
|
||||||
|
m_vtx_attr.g0.PosFormat = ComponentFormat::Float;
|
||||||
|
m_vtx_attr.g0.NormalElements = NormalComponentCount::NTB;
|
||||||
|
m_vtx_attr.g0.NormalFormat = ComponentFormat::Float;
|
||||||
|
m_vtx_attr.g0.Color0Elements = ColorComponentCount::RGBA;
|
||||||
|
m_vtx_attr.g0.Color0Comp = ColorFormat::RGBA8888;
|
||||||
|
m_vtx_attr.g0.Color1Elements = ColorComponentCount::RGBA;
|
||||||
|
m_vtx_attr.g0.Color1Comp = ColorFormat::RGBA8888;
|
||||||
|
m_vtx_attr.g0.Tex0CoordElements = TexComponentCount::ST;
|
||||||
|
m_vtx_attr.g0.Tex0CoordFormat = ComponentFormat::Float;
|
||||||
|
m_vtx_attr.g1.Tex1CoordElements = TexComponentCount::ST;
|
||||||
|
m_vtx_attr.g1.Tex1CoordFormat = ComponentFormat::Float;
|
||||||
|
m_vtx_attr.g1.Tex2CoordElements = TexComponentCount::ST;
|
||||||
|
m_vtx_attr.g1.Tex2CoordFormat = ComponentFormat::Float;
|
||||||
|
m_vtx_attr.g1.Tex3CoordElements = TexComponentCount::ST;
|
||||||
|
m_vtx_attr.g1.Tex3CoordFormat = ComponentFormat::Float;
|
||||||
|
m_vtx_attr.g1.Tex4CoordElements = TexComponentCount::ST;
|
||||||
|
m_vtx_attr.g1.Tex4CoordFormat = ComponentFormat::Float;
|
||||||
|
m_vtx_attr.g2.Tex5CoordElements = TexComponentCount::ST;
|
||||||
|
m_vtx_attr.g2.Tex5CoordFormat = ComponentFormat::Float;
|
||||||
|
m_vtx_attr.g2.Tex6CoordElements = TexComponentCount::ST;
|
||||||
|
m_vtx_attr.g2.Tex6CoordFormat = ComponentFormat::Float;
|
||||||
|
m_vtx_attr.g2.Tex7CoordElements = TexComponentCount::ST;
|
||||||
|
m_vtx_attr.g2.Tex7CoordFormat = ComponentFormat::Float;
|
||||||
|
|
||||||
|
CreateAndCheckSizes(129, 39 * sizeof(float));
|
||||||
|
|
||||||
|
// Pos matrix idx
|
||||||
|
Input<u8>(20);
|
||||||
|
// Tex matrix idx
|
||||||
|
Input<u8>(0);
|
||||||
|
Input<u8>(1);
|
||||||
|
Input<u8>(2);
|
||||||
|
Input<u8>(3);
|
||||||
|
Input<u8>(4);
|
||||||
|
Input<u8>(5);
|
||||||
|
Input<u8>(6);
|
||||||
|
Input<u8>(7);
|
||||||
|
// Position
|
||||||
|
Input(-1.0f);
|
||||||
|
Input(-2.0f);
|
||||||
|
Input(-3.0f);
|
||||||
|
// Normal
|
||||||
|
Input(-4.0f);
|
||||||
|
Input(-5.0f);
|
||||||
|
Input(-6.0f);
|
||||||
|
// Tangent
|
||||||
|
Input(-7.0f);
|
||||||
|
Input(-8.0f);
|
||||||
|
Input(-9.0f);
|
||||||
|
// Binormal
|
||||||
|
Input(-10.0f);
|
||||||
|
Input(-11.0f);
|
||||||
|
Input(-12.0f);
|
||||||
|
// Colors
|
||||||
|
Input<u32>(0x01234567);
|
||||||
|
Input<u32>(0x89abcdef);
|
||||||
|
// Texture coordinates
|
||||||
|
Input(0.1f);
|
||||||
|
Input(-0.9f);
|
||||||
|
Input(1.1f);
|
||||||
|
Input(-1.9f);
|
||||||
|
Input(2.1f);
|
||||||
|
Input(-2.9f);
|
||||||
|
Input(3.1f);
|
||||||
|
Input(-3.9f);
|
||||||
|
Input(4.1f);
|
||||||
|
Input(-4.9f);
|
||||||
|
Input(5.1f);
|
||||||
|
Input(-5.9f);
|
||||||
|
Input(6.1f);
|
||||||
|
Input(-6.9f);
|
||||||
|
Input(7.1f);
|
||||||
|
Input(-7.9f);
|
||||||
|
|
||||||
|
RunVertices(1);
|
||||||
|
|
||||||
|
// Position matrix
|
||||||
|
ASSERT_EQ(m_loader->m_native_vtx_decl.posmtx.offset, 0 * sizeof(float));
|
||||||
|
EXPECT_EQ((m_dst.Read<u32, false>()), 20u);
|
||||||
|
// Position
|
||||||
|
ASSERT_EQ(m_loader->m_native_vtx_decl.position.offset, 1 * sizeof(float));
|
||||||
|
ExpectOut(-1.0f);
|
||||||
|
ExpectOut(-2.0f);
|
||||||
|
ExpectOut(-3.0f);
|
||||||
|
// Normal
|
||||||
|
ASSERT_EQ(m_loader->m_native_vtx_decl.normals[0].offset, 4 * sizeof(float));
|
||||||
|
ExpectOut(-4.0f);
|
||||||
|
ExpectOut(-5.0f);
|
||||||
|
ExpectOut(-6.0f);
|
||||||
|
// Tangent
|
||||||
|
ASSERT_EQ(m_loader->m_native_vtx_decl.normals[1].offset, 7 * sizeof(float));
|
||||||
|
ExpectOut(-7.0f);
|
||||||
|
ExpectOut(-8.0f);
|
||||||
|
ExpectOut(-9.0f);
|
||||||
|
// Binormal
|
||||||
|
ASSERT_EQ(m_loader->m_native_vtx_decl.normals[2].offset, 10 * sizeof(float));
|
||||||
|
ExpectOut(-10.0f);
|
||||||
|
ExpectOut(-11.0f);
|
||||||
|
ExpectOut(-12.0f);
|
||||||
|
// Colors
|
||||||
|
ASSERT_EQ(m_loader->m_native_vtx_decl.colors[0].offset, 13 * sizeof(float));
|
||||||
|
EXPECT_EQ((m_dst.Read<u32, true>()), 0x01234567u);
|
||||||
|
ASSERT_EQ(m_loader->m_native_vtx_decl.colors[1].offset, 14 * sizeof(float));
|
||||||
|
EXPECT_EQ((m_dst.Read<u32, true>()), 0x89abcdefu);
|
||||||
|
// Texture coordinates and matrices (interleaved)
|
||||||
|
ASSERT_EQ(m_loader->m_native_vtx_decl.texcoords[0].offset, 15 * sizeof(float));
|
||||||
|
ExpectOut(0.1f); // S
|
||||||
|
ExpectOut(-0.9f); // T
|
||||||
|
ExpectOut(0.0f); // matrix (yes, a float)
|
||||||
|
ASSERT_EQ(m_loader->m_native_vtx_decl.texcoords[1].offset, 18 * sizeof(float));
|
||||||
|
ExpectOut(1.1f);
|
||||||
|
ExpectOut(-1.9f);
|
||||||
|
ExpectOut(1.0f);
|
||||||
|
ASSERT_EQ(m_loader->m_native_vtx_decl.texcoords[2].offset, 21 * sizeof(float));
|
||||||
|
ExpectOut(2.1f);
|
||||||
|
ExpectOut(-2.9f);
|
||||||
|
ExpectOut(2.0f);
|
||||||
|
ASSERT_EQ(m_loader->m_native_vtx_decl.texcoords[3].offset, 24 * sizeof(float));
|
||||||
|
ExpectOut(3.1f);
|
||||||
|
ExpectOut(-3.9f);
|
||||||
|
ExpectOut(3.0f);
|
||||||
|
ASSERT_EQ(m_loader->m_native_vtx_decl.texcoords[4].offset, 27 * sizeof(float));
|
||||||
|
ExpectOut(4.1f);
|
||||||
|
ExpectOut(-4.9f);
|
||||||
|
ExpectOut(4.0f);
|
||||||
|
ASSERT_EQ(m_loader->m_native_vtx_decl.texcoords[5].offset, 30 * sizeof(float));
|
||||||
|
ExpectOut(5.1f);
|
||||||
|
ExpectOut(-5.9f);
|
||||||
|
ExpectOut(5.0f);
|
||||||
|
ASSERT_EQ(m_loader->m_native_vtx_decl.texcoords[6].offset, 33 * sizeof(float));
|
||||||
|
ExpectOut(6.1f);
|
||||||
|
ExpectOut(-6.9f);
|
||||||
|
ExpectOut(6.0f);
|
||||||
|
ASSERT_EQ(m_loader->m_native_vtx_decl.texcoords[7].offset, 36 * sizeof(float));
|
||||||
|
ExpectOut(7.1f);
|
||||||
|
ExpectOut(-7.9f);
|
||||||
|
ExpectOut(7.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
class VertexLoaderNormalTest
|
||||||
|
: public VertexLoaderTest,
|
||||||
|
public ::testing::WithParamInterface<
|
||||||
|
std::tuple<VertexComponentFormat, ComponentFormat, NormalComponentCount, bool>>
|
||||||
|
{
|
||||||
|
};
|
||||||
|
INSTANTIATE_TEST_CASE_P(
|
||||||
|
AllCombinations, VertexLoaderNormalTest,
|
||||||
|
::testing::Combine(
|
||||||
|
::testing::Values(VertexComponentFormat::NotPresent, VertexComponentFormat::Direct,
|
||||||
|
VertexComponentFormat::Index8, VertexComponentFormat::Index16),
|
||||||
|
::testing::Values(ComponentFormat::UByte, ComponentFormat::Byte, ComponentFormat::UShort,
|
||||||
|
ComponentFormat::Short, ComponentFormat::Float),
|
||||||
|
::testing::Values(NormalComponentCount::N, NormalComponentCount::NTB),
|
||||||
|
::testing::Values(false, true)));
|
||||||
|
|
||||||
|
TEST_P(VertexLoaderNormalTest, NormalAll)
|
||||||
|
{
|
||||||
|
VertexComponentFormat addr;
|
||||||
|
ComponentFormat format;
|
||||||
|
NormalComponentCount elements;
|
||||||
|
bool index3;
|
||||||
|
std::tie(addr, format, elements, index3) = GetParam();
|
||||||
|
|
||||||
|
m_vtx_desc.low.Position = VertexComponentFormat::Direct;
|
||||||
|
m_vtx_attr.g0.PosFormat = ComponentFormat::Float;
|
||||||
|
m_vtx_attr.g0.PosElements = CoordComponentCount::XY;
|
||||||
|
m_vtx_attr.g0.PosFrac = 0;
|
||||||
|
m_vtx_desc.low.Normal = addr;
|
||||||
|
m_vtx_attr.g0.NormalFormat = format;
|
||||||
|
m_vtx_attr.g0.NormalElements = elements;
|
||||||
|
m_vtx_attr.g0.NormalIndex3 = index3;
|
||||||
|
|
||||||
|
const u32 in_size = [&]() -> u32 {
|
||||||
|
if (addr == VertexComponentFormat::NotPresent)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (IsIndexed(addr))
|
||||||
|
{
|
||||||
|
const u32 base_size = (addr == VertexComponentFormat::Index8) ? 1 : 2;
|
||||||
|
if (elements == NormalComponentCount::NTB)
|
||||||
|
return (index3 ? 3 : 1) * base_size;
|
||||||
|
else
|
||||||
|
return 1 * base_size;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const u32 base_count = (elements == NormalComponentCount::NTB) ? 9 : 3;
|
||||||
|
const u32 base_size = GetElementSize(format);
|
||||||
|
return base_count * base_size;
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
const u32 out_size = [&]() -> u32 {
|
||||||
|
if (addr == VertexComponentFormat::NotPresent)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
const u32 base_count = (elements == NormalComponentCount::NTB) ? 9 : 3;
|
||||||
|
return base_count * sizeof(float);
|
||||||
|
}();
|
||||||
|
|
||||||
|
CreateAndCheckSizes(2 * sizeof(float) + in_size, 2 * sizeof(float) + out_size);
|
||||||
|
|
||||||
|
auto input_with_expected_type = [&](float value) {
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case ComponentFormat::UByte:
|
||||||
|
Input<u8>(value * (1 << 7));
|
||||||
|
break;
|
||||||
|
case ComponentFormat::Byte:
|
||||||
|
Input<s8>(value * (1 << 6));
|
||||||
|
break;
|
||||||
|
case ComponentFormat::UShort:
|
||||||
|
Input<u16>(value * (1 << 15));
|
||||||
|
break;
|
||||||
|
case ComponentFormat::Short:
|
||||||
|
Input<s16>(value * (1 << 14));
|
||||||
|
break;
|
||||||
|
case ComponentFormat::Float:
|
||||||
|
default:
|
||||||
|
Input<float>(value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto create_normal = [&](int counter_base) {
|
||||||
|
if (addr == VertexComponentFormat::Direct)
|
||||||
|
{
|
||||||
|
input_with_expected_type(counter_base / 32.f);
|
||||||
|
input_with_expected_type((counter_base + 1) / 32.f);
|
||||||
|
input_with_expected_type((counter_base + 2) / 32.f);
|
||||||
|
}
|
||||||
|
else if (addr == VertexComponentFormat::Index8)
|
||||||
|
{
|
||||||
|
// We set up arrays so that this works
|
||||||
|
Input<u8>(counter_base);
|
||||||
|
}
|
||||||
|
else if (addr == VertexComponentFormat::Index16)
|
||||||
|
{
|
||||||
|
Input<u16>(counter_base);
|
||||||
|
}
|
||||||
|
// Do nothing for NotPresent
|
||||||
|
};
|
||||||
|
auto create_tangent_and_binormal = [&](int counter_base) {
|
||||||
|
if (IsIndexed(addr))
|
||||||
|
{
|
||||||
|
// With NormalIndex3, specifying the same index 3 times should give the same result
|
||||||
|
// as specifying one index in non-index3 mode (as the index is biased by bytes).
|
||||||
|
// If index3 is disabled, we don't want to write any more indices.
|
||||||
|
if (index3)
|
||||||
|
{
|
||||||
|
// Tangent
|
||||||
|
create_normal(counter_base);
|
||||||
|
// Binormal
|
||||||
|
create_normal(counter_base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Tangent
|
||||||
|
create_normal(counter_base + 3);
|
||||||
|
// Binormal
|
||||||
|
create_normal(counter_base + 6);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create our two vertices
|
||||||
|
// Position 1
|
||||||
|
Input(4.0f);
|
||||||
|
Input(8.0f);
|
||||||
|
// Normal 1
|
||||||
|
create_normal(1);
|
||||||
|
if (elements == NormalComponentCount::NTB)
|
||||||
|
{
|
||||||
|
create_tangent_and_binormal(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Position 2
|
||||||
|
Input(6.0f);
|
||||||
|
Input(12.0f);
|
||||||
|
// Normal 1
|
||||||
|
create_normal(10);
|
||||||
|
if (elements == NormalComponentCount::NTB)
|
||||||
|
{
|
||||||
|
create_tangent_and_binormal(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create an array for indexed representations
|
||||||
|
for (int i = 0; i < NUM_VERTEX_COMPONENT_ARRAYS; i++)
|
||||||
|
{
|
||||||
|
VertexLoaderManager::cached_arraybases[static_cast<CPArray>(i)] = m_src.GetPointer();
|
||||||
|
g_main_cp_state.array_strides[static_cast<CPArray>(i)] = GetElementSize(format);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 32; i++)
|
||||||
|
input_with_expected_type(i / 32.f);
|
||||||
|
|
||||||
|
// Pre-fill these values to detect if they're modified
|
||||||
|
VertexLoaderManager::binormal_cache = {42.f, 43.f, 44.f, 45.f};
|
||||||
|
VertexLoaderManager::tangent_cache = {46.f, 47.f, 48.f, 49.f};
|
||||||
|
|
||||||
|
RunVertices(2);
|
||||||
|
|
||||||
|
// First vertex, position
|
||||||
|
ExpectOut(4.0f);
|
||||||
|
ExpectOut(8.0f);
|
||||||
|
if (addr != VertexComponentFormat::NotPresent)
|
||||||
|
{
|
||||||
|
// Normal
|
||||||
|
ExpectOut(1 / 32.f);
|
||||||
|
ExpectOut(2 / 32.f);
|
||||||
|
ExpectOut(3 / 32.f);
|
||||||
|
if (elements == NormalComponentCount::NTB)
|
||||||
|
{
|
||||||
|
// Tangent
|
||||||
|
ExpectOut(4 / 32.f);
|
||||||
|
ExpectOut(5 / 32.f);
|
||||||
|
ExpectOut(6 / 32.f);
|
||||||
|
// Binormal
|
||||||
|
ExpectOut(7 / 32.f);
|
||||||
|
ExpectOut(8 / 32.f);
|
||||||
|
ExpectOut(9 / 32.f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second vertex, position
|
||||||
|
ExpectOut(6.0f);
|
||||||
|
ExpectOut(12.0f);
|
||||||
|
if (addr != VertexComponentFormat::NotPresent)
|
||||||
|
{
|
||||||
|
// Normal
|
||||||
|
ExpectOut(10 / 32.f);
|
||||||
|
ExpectOut(11 / 32.f);
|
||||||
|
ExpectOut(12 / 32.f);
|
||||||
|
if (elements == NormalComponentCount::NTB)
|
||||||
|
{
|
||||||
|
// Tangent
|
||||||
|
ExpectOut(13 / 32.f);
|
||||||
|
ExpectOut(14 / 32.f);
|
||||||
|
ExpectOut(15 / 32.f);
|
||||||
|
// Binormal
|
||||||
|
ExpectOut(16 / 32.f);
|
||||||
|
ExpectOut(17 / 32.f);
|
||||||
|
ExpectOut(18 / 32.f);
|
||||||
|
|
||||||
|
EXPECT_EQ(VertexLoaderManager::tangent_cache[0], 13 / 32.f);
|
||||||
|
EXPECT_EQ(VertexLoaderManager::tangent_cache[1], 14 / 32.f);
|
||||||
|
EXPECT_EQ(VertexLoaderManager::tangent_cache[2], 15 / 32.f);
|
||||||
|
// Last index is padding/junk
|
||||||
|
EXPECT_EQ(VertexLoaderManager::binormal_cache[0], 16 / 32.f);
|
||||||
|
EXPECT_EQ(VertexLoaderManager::binormal_cache[1], 17 / 32.f);
|
||||||
|
EXPECT_EQ(VertexLoaderManager::binormal_cache[2], 18 / 32.f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addr == VertexComponentFormat::NotPresent || elements == NormalComponentCount::N)
|
||||||
|
{
|
||||||
|
// Expect these to not be written
|
||||||
|
EXPECT_EQ(VertexLoaderManager::binormal_cache[0], 42.f);
|
||||||
|
EXPECT_EQ(VertexLoaderManager::binormal_cache[1], 43.f);
|
||||||
|
EXPECT_EQ(VertexLoaderManager::binormal_cache[2], 44.f);
|
||||||
|
EXPECT_EQ(VertexLoaderManager::binormal_cache[3], 45.f);
|
||||||
|
EXPECT_EQ(VertexLoaderManager::tangent_cache[0], 46.f);
|
||||||
|
EXPECT_EQ(VertexLoaderManager::tangent_cache[1], 47.f);
|
||||||
|
EXPECT_EQ(VertexLoaderManager::tangent_cache[2], 48.f);
|
||||||
|
EXPECT_EQ(VertexLoaderManager::tangent_cache[3], 49.f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For gtest, which doesn't know about our fmt::formatters by default
|
||||||
|
static void PrintTo(const VertexComponentFormat& t, std::ostream* os)
|
||||||
|
{
|
||||||
|
*os << fmt::to_string(t);
|
||||||
|
}
|
||||||
|
static void PrintTo(const ComponentFormat& t, std::ostream* os)
|
||||||
|
{
|
||||||
|
*os << fmt::to_string(t);
|
||||||
|
}
|
||||||
|
static void PrintTo(const CoordComponentCount& t, std::ostream* os)
|
||||||
|
{
|
||||||
|
*os << fmt::to_string(t);
|
||||||
|
}
|
||||||
|
static void PrintTo(const NormalComponentCount& t, std::ostream* os)
|
||||||
|
{
|
||||||
|
*os << fmt::to_string(t);
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user