aboutsummaryrefslogtreecommitdiff
path: root/src/tests/video_core/vertex_repack.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/tests/video_core/vertex_repack.cpp')
-rw-r--r--src/tests/video_core/vertex_repack.cpp126
1 files changed, 126 insertions, 0 deletions
diff --git a/src/tests/video_core/vertex_repack.cpp b/src/tests/video_core/vertex_repack.cpp
new file mode 100644
index 0000000000..5e21e3714c
--- /dev/null
+++ b/src/tests/video_core/vertex_repack.cpp
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include <catch2/catch_test_macros.hpp>
+#include "video_core/renderer_vulkan/vertex_repack.h"
+
+namespace {
+using Regs = Tegra::Engines::Maxwell3D::Regs;
+using Size = Regs::VertexAttribute::Size;
+
+Regs MakeLayout() {
+ Regs regs{};
+ for (auto& attr : regs.vertex_attrib_format) {
+ attr.constant.Assign(1);
+ }
+ regs.vertex_streams[0].stride.Assign(69);
+ auto& position = regs.vertex_attrib_format[0];
+ position.constant.Assign(0);
+ position.size.Assign(Size::Size_R32_G32_B32);
+ auto& packed = regs.vertex_attrib_format[1];
+ packed.constant.Assign(0);
+ packed.offset.Assign(65);
+ packed.size.Assign(Size::Size_A2_B10_G10_R10);
+ return regs;
+}
+}
+
+TEST_CASE("Vertex repacking preserves aligned fields and relocates packed tail", "[vertex_repack]") {
+ const auto regs = MakeLayout();
+ const auto mask = Vulkan::VertexRepackMask(regs);
+ REQUIRE(mask == 1);
+ REQUIRE(Vulkan::RepackedVertexOffset(mask, 0, 0) == 0);
+ REQUIRE(Vulkan::RepackedVertexOffset(mask, 0, 64) == 64);
+ REQUIRE(Vulkan::RepackedVertexOffset(mask, 0, 65) == 68);
+ REQUIRE(Vulkan::RepackedVertexStride(mask, 0, 69) == 72);
+ REQUIRE(Vulkan::RepackedVertexStride(mask, 1, 16) == 16);
+}
+
+TEST_CASE("Vertex repacking excludes overlapping and oversized attributes", "[vertex_repack]") {
+ auto regs = MakeLayout();
+ auto& attr = regs.vertex_attrib_format[2];
+ attr.constant.Assign(0);
+ attr.offset.Assign(64);
+ attr.size.Assign(Size::Size_R32);
+ REQUIRE(Vulkan::VertexRepackMask(regs) == 0);
+ attr.offset.Assign(68);
+ REQUIRE(Vulkan::VertexRepackMask(regs) == 0);
+ attr.constant.Assign(1);
+ REQUIRE(Vulkan::VertexRepackMask(regs) == 1);
+}
+
+TEST_CASE("Vertex repacking ignores constant invalid formats", "[vertex_repack]") {
+ auto regs = MakeLayout();
+ REQUIRE(Vulkan::VertexRepackMask(regs) == 1);
+ regs.vertex_attrib_format[2].constant.Assign(0);
+ REQUIRE(Vulkan::VertexRepackMask(regs) == 0);
+}
+
+TEST_CASE("Vertex repacking selects bindings independently", "[vertex_repack]") {
+ auto regs = MakeLayout();
+ regs.vertex_streams[31].stride.Assign(69);
+ auto& attr = regs.vertex_attrib_format[2];
+ attr = regs.vertex_attrib_format[1];
+ attr.buffer.Assign(31);
+ REQUIRE(Vulkan::VertexRepackMask(regs) == 0x80000001U);
+ regs.vertex_streams[0].stride.Assign(72);
+ REQUIRE(Vulkan::VertexRepackMask(regs) == 0x80000000U);
+ REQUIRE(Vulkan::RepackedVertexOffset(0x80000000U, 0, 65) == 65);
+}
+
+TEST_CASE("Vertex repacking requires the captured tail and stride", "[vertex_repack]") {
+ auto regs = MakeLayout();
+ regs.vertex_streams[0].stride.Assign(68);
+ REQUIRE(Vulkan::VertexRepackMask(regs) == 0);
+ regs.vertex_streams[0].stride.Assign(69);
+ regs.vertex_attrib_format[1].offset.Assign(64);
+ REQUIRE(Vulkan::VertexRepackMask(regs) == 0);
+}
+
+TEST_CASE("Static vertex repacking follows the device stride alignment", "[vertex_repack]") {
+ REQUIRE(Vulkan::VertexRepackInputSupported(true, false));
+ REQUIRE_FALSE(Vulkan::VertexRepackInputSupported(false, false));
+ REQUIRE(Vulkan::VertexRepackInputSupported(false, true));
+ REQUIRE_FALSE(Vulkan::StaticVertexRepackSelected(nullptr, 1));
+ REQUIRE(Vulkan::StaticVertexRepackSelected(nullptr, 4));
+ REQUIRE(Vulkan::StaticVertexRepackSelected("1", 1));
+ REQUIRE_FALSE(Vulkan::StaticVertexRepackSelected("0", 4));
+ REQUIRE(Vulkan::StaticVertexRepackSelected("other", 4));
+}
+
+TEST_CASE("Vertex repack planning keeps descriptor offsets aligned", "[vertex_repack]") {
+ for (const u32 alignment : {4U, 16U, 64U, 256U, 4096U}) {
+ for (const u32 offset : {0U, 1U, 3U, 255U, 4099U}) {
+ const auto plan = Vulkan::MakeVertexRepackParameters(offset, 70, alignment, 1U << 20,
+ 65535, 1U << 20);
+ REQUIRE(plan.has_value());
+ REQUIRE(plan->source_base % alignment == 0);
+ REQUIRE(plan->source_base + plan->source_bias == offset);
+ REQUIRE(plan->vertices == 2);
+ REQUIRE(plan->output_size == 144);
+ REQUIRE(plan->source_range >= plan->source_bias + 70);
+ REQUIRE(plan->source_range % 4 == 0);
+ REQUIRE(plan->dispatch_groups == 1);
+ }
+ }
+}
+
+TEST_CASE("Vertex repack planning rejects overflow and device-limit violations", "[vertex_repack]") {
+ REQUIRE_FALSE(Vulkan::MakeVertexRepackParameters(0, 0, 256, 1U << 20, 65535, 1U << 20));
+ REQUIRE_FALSE(Vulkan::MakeVertexRepackParameters(0, 69, 0, 1U << 20, 65535, 1U << 20));
+ REQUIRE_FALSE(Vulkan::MakeVertexRepackParameters(0, 69, 256, 71, 65535, 1U << 20));
+ REQUIRE_FALSE(Vulkan::MakeVertexRepackParameters(255, 69, 256, 323, 65535, 1U << 20));
+ REQUIRE_FALSE(Vulkan::MakeVertexRepackParameters(0, 69, 256, 1U << 20, 0, 1U << 20));
+ REQUIRE_FALSE(
+ Vulkan::MakeVertexRepackParameters(0, 0xffffffffU, 256, ~u64{0}, 0xffffffffU, ~u64{0}));
+ const auto one = Vulkan::MakeVertexRepackParameters(0, 69, 256, 72, 1, 72);
+ REQUIRE(one.has_value());
+ REQUIRE(one->source_range == 72);
+}
+
+TEST_CASE("Vertex repack planning keeps the source range inside the buffer", "[vertex_repack]") {
+ const auto clamped = Vulkan::MakeVertexRepackParameters(0, 69, 256, 1U << 20, 65535, 69);
+ REQUIRE(clamped.has_value());
+ REQUIRE(clamped->source_range == 69);
+ REQUIRE_FALSE(Vulkan::MakeVertexRepackParameters(0, 69, 256, 1U << 20, 65535, 68));
+ REQUIRE_FALSE(Vulkan::MakeVertexRepackParameters(300, 69, 256, 1U << 20, 65535, 256));
+}