1 /*
2 * Copyright 2024 Alyssa Rosenzweig
3 * Copyright 2022 Collabora, Ltd.
4 * SPDX-License-Identifier: MIT
5 *
6 */
7
8 #include "layout.h"
9
10 #include "util/format/u_format.h"
11 #include <gtest/gtest.h>
12
13 /*
14 * Reference tiling algorithm, written for clarity rather than performance. See
15 * docs/drivers/asahi.rst for details on the format.
16 */
17
18 static unsigned
z_order(unsigned x,unsigned y)19 z_order(unsigned x, unsigned y)
20 {
21 unsigned out = 0;
22
23 for (unsigned i = 0; i < 8; ++i) {
24 unsigned bit = (1 << (2 * i));
25
26 if (x & (1 << i))
27 out |= bit;
28
29 if (y & (1 << i))
30 out |= bit << 1;
31 }
32
33 return out;
34 }
35
36 /* x/y are in blocks */
37 static unsigned
tiled_offset_el(struct ail_layout * layout,unsigned level,unsigned x_el,unsigned y_el)38 tiled_offset_el(struct ail_layout *layout, unsigned level, unsigned x_el,
39 unsigned y_el)
40 {
41 unsigned x_tl = x_el / layout->tilesize_el[level].width_el;
42 unsigned y_tl = y_el / layout->tilesize_el[level].height_el;
43
44 unsigned offs_x_el = x_el % layout->tilesize_el[level].width_el;
45 unsigned offs_y_el = y_el % layout->tilesize_el[level].height_el;
46
47 unsigned offs_in_tile_el = z_order(offs_x_el, offs_y_el);
48
49 unsigned offs_row_el =
50 y_tl *
51 align(layout->stride_el[level], layout->tilesize_el[level].width_el) *
52 layout->tilesize_el[level].height_el;
53
54 unsigned offs_col_el = x_tl * layout->tilesize_el[level].width_el *
55 layout->tilesize_el[level].height_el;
56
57 return offs_row_el + offs_col_el + offs_in_tile_el;
58 }
59
60 static unsigned
linear_offset_B(unsigned x_el,unsigned y_el,unsigned stride_B,unsigned blocksize_B)61 linear_offset_B(unsigned x_el, unsigned y_el, unsigned stride_B,
62 unsigned blocksize_B)
63 {
64 return (stride_B * y_el) + (x_el * blocksize_B);
65 }
66
67 static void
ref_access_tiled(uint8_t * tiled,uint8_t * linear,struct ail_layout * layout,unsigned region_x_px,unsigned region_y_px,unsigned w_px,unsigned h_px,uint32_t linear_stride_B,bool dst_is_tiled)68 ref_access_tiled(uint8_t *tiled, uint8_t *linear, struct ail_layout *layout,
69 unsigned region_x_px, unsigned region_y_px, unsigned w_px,
70 unsigned h_px, uint32_t linear_stride_B, bool dst_is_tiled)
71 {
72 unsigned blocksize_B = util_format_get_blocksize(layout->format);
73
74 unsigned w_el = util_format_get_nblocksx(layout->format, w_px);
75 unsigned h_el = util_format_get_nblocksy(layout->format, h_px);
76
77 unsigned region_x_el = util_format_get_nblocksx(layout->format, region_x_px);
78 unsigned region_y_el = util_format_get_nblocksy(layout->format, region_y_px);
79
80 for (unsigned linear_y_el = 0; linear_y_el < h_el; ++linear_y_el) {
81 for (unsigned linear_x_el = 0; linear_x_el < w_el; ++linear_x_el) {
82 unsigned tiled_x_el = region_x_el + linear_x_el;
83 unsigned tiled_y_el = region_y_el + linear_y_el;
84
85 uint8_t *linear_ptr =
86 linear + linear_offset_B(linear_x_el, linear_y_el, linear_stride_B,
87 blocksize_B);
88
89 uint8_t *tiled_ptr =
90 tiled +
91 (blocksize_B * tiled_offset_el(layout, 0, tiled_x_el, tiled_y_el));
92
93 if (dst_is_tiled) {
94 memcpy(tiled_ptr, linear_ptr, blocksize_B);
95 } else {
96 memcpy(linear_ptr, tiled_ptr, blocksize_B);
97 }
98 }
99 }
100 }
101
102 /*
103 * Helper to build test cases for tiled texture access. This test suite compares
104 * the above reference tiling algorithm to the optimized algorithm used in
105 * production.
106 */
107 static void
test(unsigned width,unsigned height,unsigned rx,unsigned ry,unsigned rw,unsigned rh,unsigned linear_stride,enum pipe_format format,bool store)108 test(unsigned width, unsigned height, unsigned rx, unsigned ry, unsigned rw,
109 unsigned rh, unsigned linear_stride, enum pipe_format format, bool store)
110 {
111 unsigned bpp = util_format_get_blocksize(format);
112 struct ail_layout layout = {
113 .width_px = width,
114 .height_px = height,
115 .depth_px = 1,
116 .sample_count_sa = 1,
117 .levels = 1,
118 .tiling = AIL_TILING_TWIDDLED,
119 .format = format,
120 };
121
122 ail_make_miptree(&layout);
123
124 uint8_t *tiled = (uint8_t *)calloc(bpp, layout.size_B);
125 uint8_t *linear = (uint8_t *)calloc(bpp, rh * linear_stride);
126 uint8_t *ref =
127 (uint8_t *)calloc(bpp, store ? layout.size_B : (rh * linear_stride));
128
129 if (store) {
130 for (unsigned i = 0; i < bpp * rh * linear_stride; ++i) {
131 linear[i] = (i & 0xFF);
132 }
133
134 ail_tile(tiled, linear, &layout, 0, linear_stride, rx, ry, rw, rh);
135 ref_access_tiled(ref, linear, &layout, rx, ry, rw, rh, linear_stride,
136 true);
137
138 EXPECT_EQ(memcmp(ref, tiled, layout.size_B), 0);
139 } else {
140 for (unsigned i = 0; i < layout.size_B; ++i) {
141 tiled[i] = (i & 0xFF);
142 }
143
144 ail_detile(tiled, linear, &layout, 0, linear_stride, rx, ry, rw, rh);
145 ref_access_tiled(tiled, ref, &layout, rx, ry, rw, rh, linear_stride,
146 false);
147
148 EXPECT_EQ(memcmp(ref, linear, bpp * rh * linear_stride), 0);
149 }
150
151 free(ref);
152 free(tiled);
153 free(linear);
154 }
155
156 static void
test_ldst(unsigned width,unsigned height,unsigned rx,unsigned ry,unsigned rw,unsigned rh,unsigned linear_stride,enum pipe_format format)157 test_ldst(unsigned width, unsigned height, unsigned rx, unsigned ry,
158 unsigned rw, unsigned rh, unsigned linear_stride,
159 enum pipe_format format)
160 {
161 test(width, height, rx, ry, rw, rh, linear_stride, format, true);
162 test(width, height, rx, ry, rw, rh, linear_stride, format, false);
163 }
164
TEST(Twiddling,RegularFormats)165 TEST(Twiddling, RegularFormats)
166 {
167 test_ldst(233, 173, 0, 0, 233, 173, 233, PIPE_FORMAT_R8_UINT);
168 test_ldst(233, 173, 0, 0, 233, 173, 233 * 2, PIPE_FORMAT_R8G8_UINT);
169 test_ldst(233, 173, 0, 0, 233, 173, 233 * 4, PIPE_FORMAT_R32_UINT);
170 test_ldst(173, 143, 0, 0, 173, 143, 173 * 8, PIPE_FORMAT_R32G32_UINT);
171 test_ldst(133, 143, 0, 0, 133, 143, 133 * 16, PIPE_FORMAT_R32G32B32A32_UINT);
172 }
173
TEST(Twiddling,UnpackedStrides)174 TEST(Twiddling, UnpackedStrides)
175 {
176 test_ldst(213, 17, 0, 0, 213, 17, 369 * 1, PIPE_FORMAT_R8_SINT);
177 test_ldst(213, 17, 0, 0, 213, 17, 369 * 2, PIPE_FORMAT_R8G8_SINT);
178 test_ldst(213, 17, 0, 0, 213, 17, 369 * 4, PIPE_FORMAT_R32_SINT);
179 test_ldst(213, 17, 0, 0, 213, 17, 369 * 8, PIPE_FORMAT_R32G32_SINT);
180 test_ldst(213, 17, 0, 0, 213, 17, 369 * 16, PIPE_FORMAT_R32G32B32A32_SINT);
181 }
182
TEST(Twiddling,PartialAccess)183 TEST(Twiddling, PartialAccess)
184 {
185 test_ldst(283, 171, 3, 1, 131, 7, 369 * 1, PIPE_FORMAT_R8_UNORM);
186 test_ldst(283, 171, 3, 1, 131, 7, 369 * 2, PIPE_FORMAT_R8G8_UNORM);
187 test_ldst(283, 171, 3, 1, 131, 7, 369 * 4, PIPE_FORMAT_R32_UNORM);
188 test_ldst(283, 171, 3, 1, 131, 7, 369 * 8, PIPE_FORMAT_R32G32_UNORM);
189 test_ldst(283, 171, 3, 1, 131, 7, 369 * 16, PIPE_FORMAT_R32G32B32A32_UNORM);
190 }
191
TEST(Twiddling,ETC)192 TEST(Twiddling, ETC)
193 {
194 /* Block alignment assumed */
195 test_ldst(32, 32, 0, 0, 32, 32, 512, PIPE_FORMAT_ETC1_RGB8);
196 test_ldst(32, 256, 0, 0, 32, 256, 512, PIPE_FORMAT_ETC2_RGB8A1);
197 test_ldst(32, 512, 0, 0, 32, 512, 512, PIPE_FORMAT_ETC2_RG11_SNORM);
198 }
199
TEST(Twiddling,PartialETC)200 TEST(Twiddling, PartialETC)
201 {
202 /* Block alignment assumed */
203 test_ldst(32, 32, 4, 8, 16, 12, 512, PIPE_FORMAT_ETC1_RGB8);
204 test_ldst(32, 32, 4, 8, 16, 12, 512, PIPE_FORMAT_ETC2_RGB8A1);
205 test_ldst(32, 32, 4, 8, 16, 12, 512, PIPE_FORMAT_ETC2_RG11_SNORM);
206 }
207
TEST(Twiddling,DXT)208 TEST(Twiddling, DXT)
209 {
210 /* Block alignment assumed */
211 test_ldst(32, 32, 0, 0, 32, 32, 512, PIPE_FORMAT_DXT1_RGB);
212 test_ldst(32, 32, 0, 0, 32, 32, 512, PIPE_FORMAT_DXT3_RGBA);
213 test_ldst(32, 32, 0, 0, 32, 32, 512, PIPE_FORMAT_DXT5_RGBA);
214 }
215
TEST(Twiddling,PartialDXT)216 TEST(Twiddling, PartialDXT)
217 {
218 /* Block alignment assumed */
219 test_ldst(32, 32, 4, 8, 16, 12, 512, PIPE_FORMAT_DXT1_RGB);
220 test_ldst(32, 32, 4, 8, 16, 12, 512, PIPE_FORMAT_DXT3_RGBA);
221 test_ldst(32, 32, 4, 8, 16, 12, 512, PIPE_FORMAT_DXT5_RGBA);
222 }
223
TEST(Twiddling,ASTC)224 TEST(Twiddling, ASTC)
225 {
226 /* Block alignment assumed */
227 test_ldst(40, 40, 0, 0, 40, 40, 512, PIPE_FORMAT_ASTC_4x4);
228 test_ldst(50, 40, 0, 0, 50, 40, 512, PIPE_FORMAT_ASTC_5x4);
229 test_ldst(50, 50, 0, 0, 50, 50, 512, PIPE_FORMAT_ASTC_5x5);
230 }
231
TEST(Twiddling,PartialASTC)232 TEST(Twiddling, PartialASTC)
233 {
234 /* Block alignment assumed */
235 test_ldst(40, 40, 4, 4, 16, 8, 512, PIPE_FORMAT_ASTC_4x4);
236 test_ldst(50, 40, 5, 4, 10, 8, 512, PIPE_FORMAT_ASTC_5x4);
237 test_ldst(50, 50, 5, 5, 10, 10, 512, PIPE_FORMAT_ASTC_5x5);
238 }
239