From 1ee6824324979b186baca2b5266be9c20e88c78a Mon Sep 17 00:00:00 2001 From: Pokechu22 Date: Sat, 2 Dec 2023 15:39:43 -0800 Subject: [PATCH] VertexLoaderTest: Add test for skipped texture coordinates Jimmie Johnson's Anything with an Engine is known to use texture coordinate 7 (and only texture coordinate 7) in some cases. There are a lot of possible edge-cases, so this test brute-forces all combinations with coordinates 0, 1, and 2. --- Source/Core/VideoCommon/CPMemory.h | 96 ++++++++++ .../VideoCommon/VertexLoaderTest.cpp | 178 ++++++++++++++++++ 2 files changed, 274 insertions(+) diff --git a/Source/Core/VideoCommon/CPMemory.h b/Source/Core/VideoCommon/CPMemory.h index 098cf1f288..1d55d7bf3c 100644 --- a/Source/Core/VideoCommon/CPMemory.h +++ b/Source/Core/VideoCommon/CPMemory.h @@ -575,6 +575,102 @@ struct VAT return 0; } } + void SetTexElements(size_t idx, TexComponentCount value) + { + switch (idx) + { + case 0: + g0.Tex0CoordElements = value; + return; + case 1: + g1.Tex1CoordElements = value; + return; + case 2: + g1.Tex2CoordElements = value; + return; + case 3: + g1.Tex3CoordElements = value; + return; + case 4: + g1.Tex4CoordElements = value; + return; + case 5: + g2.Tex5CoordElements = value; + return; + case 6: + g2.Tex6CoordElements = value; + return; + case 7: + g2.Tex7CoordElements = value; + return; + default: + PanicAlertFmt("Invalid tex coord index {}", idx); + } + } + void SetTexFormat(size_t idx, ComponentFormat value) + { + switch (idx) + { + case 0: + g0.Tex0CoordFormat = value; + return; + case 1: + g1.Tex1CoordFormat = value; + return; + case 2: + g1.Tex2CoordFormat = value; + return; + case 3: + g1.Tex3CoordFormat = value; + return; + case 4: + g1.Tex4CoordFormat = value; + return; + case 5: + g2.Tex5CoordFormat = value; + return; + case 6: + g2.Tex6CoordFormat = value; + return; + case 7: + g2.Tex7CoordFormat = value; + return; + default: + PanicAlertFmt("Invalid tex coord index {}", idx); + } + } + void SetTexFrac(size_t idx, u8 value) + { + switch (idx) + { + case 0: + g0.Tex0Frac = value; + return; + case 1: + g1.Tex1Frac = value; + return; + case 2: + g1.Tex2Frac = value; + return; + case 3: + g1.Tex3Frac = value; + return; + case 4: + g2.Tex4Frac = value; + return; + case 5: + g2.Tex5Frac = value; + return; + case 6: + g2.Tex6Frac = value; + return; + case 7: + g2.Tex7Frac = value; + return; + default: + PanicAlertFmt("Invalid tex coord index {}", idx); + } + } }; template <> struct fmt::formatter diff --git a/Source/UnitTests/VideoCommon/VertexLoaderTest.cpp b/Source/UnitTests/VideoCommon/VertexLoaderTest.cpp index 828716e8a9..f467fcd23b 100644 --- a/Source/UnitTests/VideoCommon/VertexLoaderTest.cpp +++ b/Source/UnitTests/VideoCommon/VertexLoaderTest.cpp @@ -859,6 +859,184 @@ TEST_P(VertexLoaderSkippedColorsTest, SkippedColors) EXPECT_EQ((m_dst.Read()), 0x08090a0bu); } +class VertexLoaderSkippedTexCoordsTest : public VertexLoaderTest, + public ::testing::WithParamInterface +{ +public: + static constexpr u32 NUM_COMPONENTS_TO_TEST = 3; + static constexpr u32 NUM_PARAMETERS_PER_COMPONENT = 3; + static constexpr u32 NUM_COMBINATIONS = + 1 << (NUM_COMPONENTS_TO_TEST * NUM_PARAMETERS_PER_COMPONENT); +}; +INSTANTIATE_TEST_SUITE_P(AllCombinations, VertexLoaderSkippedTexCoordsTest, + ::testing::Range(0u, VertexLoaderSkippedTexCoordsTest::NUM_COMBINATIONS)); + +TEST_P(VertexLoaderSkippedTexCoordsTest, SkippedTextures) +{ + std::array enable_tex, enable_matrix, use_st; + const u32 param = GetParam(); + for (u32 component = 0; component < NUM_COMPONENTS_TO_TEST; component++) + { + const u32 bits = param >> (component * NUM_PARAMETERS_PER_COMPONENT); + enable_tex[component] = (bits & 1); + enable_matrix[component] = (bits & 2); + use_st[component] = (bits & 4); + } + + size_t input_size = 1; + size_t output_size = 3 * sizeof(float); + + std::array component_enabled{}; + std::array component_offset{}; + + m_vtx_desc.low.Position = VertexComponentFormat::Index8; + m_vtx_attr.g0.PosElements = CoordComponentCount::XYZ; + m_vtx_attr.g0.PosFormat = ComponentFormat::Float; + + for (size_t i = 0; i < NUM_COMPONENTS_TO_TEST; i++) + { + if (enable_matrix[i] || enable_tex[i]) + { + component_enabled[i] = true; + component_offset[i] = output_size; + if (enable_matrix[i]) + { + output_size += 3 * sizeof(float); + } + else + { + if (use_st[i]) + { + output_size += 2 * sizeof(float); + } + else + { + output_size += sizeof(float); + } + } + } + if (enable_matrix[i]) + { + m_vtx_desc.low.TexMatIdx[i] = enable_matrix[i]; + input_size++; + } + if (enable_tex[i]) + { + m_vtx_desc.high.TexCoord[i] = VertexComponentFormat::Index8; + input_size++; + } + + m_vtx_attr.SetTexElements(i, use_st[i] ? TexComponentCount::ST : TexComponentCount::S); + m_vtx_attr.SetTexFormat(i, ComponentFormat::Float); + m_vtx_attr.SetTexFrac(i, 0); + } + + CreateAndCheckSizes(input_size, output_size); + + // Vertex 0 + for (size_t i = 0; i < NUM_COMPONENTS_TO_TEST; i++) + { + if (enable_matrix[i]) + Input(u8(20 + i)); + } + Input(1); // Position + for (size_t i = 0; i < NUM_COMPONENTS_TO_TEST; i++) + { + if (enable_tex[i]) + Input(1); + } + // Vertex 1 + for (size_t i = 0; i < NUM_COMPONENTS_TO_TEST; i++) + { + if (enable_matrix[i]) + Input(u8(10 + i)); + } + Input(0); // Position + for (size_t i = 0; i < NUM_COMPONENTS_TO_TEST; i++) + { + if (enable_tex[i]) + Input(0); + } + // Position array + VertexLoaderManager::cached_arraybases[CPArray::Position] = m_src.GetPointer(); + g_main_cp_state.array_strides[CPArray::Position] = + sizeof(float); // so 1, 2, 3 for index 0; 2, 3, 4 for index 1 + Input(1.f); + Input(2.f); + Input(3.f); + Input(4.f); + // Texture coord arrays + for (u8 i = 0; i < NUM_COMPONENTS_TO_TEST; i++) + { + VertexLoaderManager::cached_arraybases[CPArray::TexCoord0 + i] = m_src.GetPointer(); + g_main_cp_state.array_strides[CPArray::TexCoord0 + i] = 2 * sizeof(float); + Input(i * 100 + 11); + Input(i * 100 + 12); + Input(i * 100 + 21); + Input(i * 100 + 22); + } + + for (size_t i = 0; i < NUM_COMPONENTS_TO_TEST; i++) + { + ASSERT_EQ(m_loader->m_native_vtx_decl.texcoords[i].enable, component_enabled[i]); + if (component_enabled[i]) + ASSERT_EQ(m_loader->m_native_vtx_decl.texcoords[i].offset, component_offset[i]); + } + + RunVertices(2); + + // Vertex 0 + ExpectOut(2); + ExpectOut(3); + ExpectOut(4); + for (size_t i = 0; i < NUM_COMPONENTS_TO_TEST; i++) + { + size_t num_read = 0; + if (enable_tex[i]) + { + ExpectOut(i * 100 + 21); + num_read++; + if (use_st[i]) + { + ExpectOut(i * 100 + 22); + num_read++; + } + } + if (enable_matrix[i]) + { + // With a matrix there are always 3 components; otherwise-unused components should be 0 + while (num_read++ < 2) + ExpectOut(0); + ExpectOut(20 + i); + } + } + // Vertex 1 + ExpectOut(1); + ExpectOut(2); + ExpectOut(3); + for (size_t i = 0; i < NUM_COMPONENTS_TO_TEST; i++) + { + size_t num_read = 0; + if (enable_tex[i]) + { + ExpectOut(i * 100 + 11); + num_read++; + if (use_st[i]) + { + ExpectOut(i * 100 + 12); + num_read++; + } + } + if (enable_matrix[i]) + { + // With a matrix there are always 3 components; otherwise-unused components should be 0 + while (num_read++ < 2) + ExpectOut(0); + ExpectOut(10 + i); + } + } +} + // For gtest, which doesn't know about our fmt::formatters by default static void PrintTo(const VertexComponentFormat& t, std::ostream* os) {