1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
|
// 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 requires explicit experimental opt-in", "[vertex_repack]") {
REQUIRE(Vulkan::VertexRepackInputSupported(true, false));
REQUIRE_FALSE(Vulkan::VertexRepackInputSupported(false, false));
REQUIRE(Vulkan::VertexRepackInputSupported(false, true));
}
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);
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));
REQUIRE_FALSE(Vulkan::MakeVertexRepackParameters(0, 69, 0, 1U << 20, 65535));
REQUIRE_FALSE(Vulkan::MakeVertexRepackParameters(0, 69, 256, 71, 65535));
REQUIRE_FALSE(Vulkan::MakeVertexRepackParameters(255, 69, 256, 323, 65535));
REQUIRE_FALSE(Vulkan::MakeVertexRepackParameters(0, 69, 256, 1U << 20, 0));
REQUIRE_FALSE(Vulkan::MakeVertexRepackParameters(0, 0xffffffffU, 256, ~u64{0}, 0xffffffffU));
const auto one = Vulkan::MakeVertexRepackParameters(0, 69, 256, 72, 1);
REQUIRE(one.has_value());
REQUIRE(one->source_range == 72);
}
|