blob: f95d9b084bff39f880ad23fce278e98a5c40bc2c [file] [log] [blame] [edit]
/*
* Copyright 2024 Alyssa Rosenzweig
* Copyright 2022 Collabora, Ltd.
* SPDX-License-Identifier: MIT
*
*/
#include "layout.h"
#include "util/format/u_format.h"
#include <gtest/gtest.h>
/*
* Reference tiling algorithm, written for clarity rather than performance. See
* docs/drivers/asahi.rst for details on the format.
*/
static unsigned
z_order(unsigned x, unsigned y)
{
unsigned out = 0;
for (unsigned i = 0; i < 8; ++i) {
unsigned bit = (1 << (2 * i));
if (x & (1 << i))
out |= bit;
if (y & (1 << i))
out |= bit << 1;
}
return out;
}
/* x/y are in blocks */
static unsigned
tiled_offset_el(struct ail_layout *layout, unsigned level, unsigned x_el,
unsigned y_el)
{
unsigned x_tl = x_el / layout->tilesize_el[level].width_el;
unsigned y_tl = y_el / layout->tilesize_el[level].height_el;
unsigned offs_x_el = x_el % layout->tilesize_el[level].width_el;
unsigned offs_y_el = y_el % layout->tilesize_el[level].height_el;
unsigned offs_in_tile_el = z_order(offs_x_el, offs_y_el);
unsigned offs_row_el =
y_tl *
align(layout->stride_el[level], layout->tilesize_el[level].width_el) *
layout->tilesize_el[level].height_el;
unsigned offs_col_el = x_tl * layout->tilesize_el[level].width_el *
layout->tilesize_el[level].height_el;
return offs_row_el + offs_col_el + offs_in_tile_el;
}
static unsigned
linear_offset_B(unsigned x_el, unsigned y_el, unsigned stride_B,
unsigned blocksize_B)
{
return (stride_B * y_el) + (x_el * blocksize_B);
}
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)
{
unsigned blocksize_B = util_format_get_blocksize(layout->format);
unsigned w_el = util_format_get_nblocksx(layout->format, w_px);
unsigned h_el = util_format_get_nblocksy(layout->format, h_px);
unsigned region_x_el = util_format_get_nblocksx(layout->format, region_x_px);
unsigned region_y_el = util_format_get_nblocksy(layout->format, region_y_px);
for (unsigned linear_y_el = 0; linear_y_el < h_el; ++linear_y_el) {
for (unsigned linear_x_el = 0; linear_x_el < w_el; ++linear_x_el) {
unsigned tiled_x_el = region_x_el + linear_x_el;
unsigned tiled_y_el = region_y_el + linear_y_el;
uint8_t *linear_ptr =
linear + linear_offset_B(linear_x_el, linear_y_el, linear_stride_B,
blocksize_B);
uint8_t *tiled_ptr =
tiled +
(blocksize_B * tiled_offset_el(layout, 0, tiled_x_el, tiled_y_el));
if (dst_is_tiled) {
memcpy(tiled_ptr, linear_ptr, blocksize_B);
} else {
memcpy(linear_ptr, tiled_ptr, blocksize_B);
}
}
}
}
/*
* Helper to build test cases for tiled texture access. This test suite compares
* the above reference tiling algorithm to the optimized algorithm used in
* production.
*/
static void
test(unsigned width, unsigned height, unsigned rx, unsigned ry, unsigned rw,
unsigned rh, unsigned linear_stride, enum pipe_format format, bool store)
{
unsigned bpp = util_format_get_blocksize(format);
struct ail_layout layout = {
.width_px = width,
.height_px = height,
.depth_px = 1,
.sample_count_sa = 1,
.levels = 1,
.tiling = AIL_TILING_TWIDDLED,
.format = format,
};
ail_make_miptree(&layout);
uint8_t *tiled = (uint8_t *)calloc(bpp, layout.size_B);
uint8_t *linear = (uint8_t *)calloc(bpp, rh * linear_stride);
uint8_t *ref =
(uint8_t *)calloc(bpp, store ? layout.size_B : (rh * linear_stride));
if (store) {
for (unsigned i = 0; i < bpp * rh * linear_stride; ++i) {
linear[i] = (i & 0xFF);
}
ail_tile(tiled, linear, &layout, 0, linear_stride, rx, ry, rw, rh);
ref_access_tiled(ref, linear, &layout, rx, ry, rw, rh, linear_stride,
true);
EXPECT_EQ(memcmp(ref, tiled, layout.size_B), 0);
} else {
for (unsigned i = 0; i < layout.size_B; ++i) {
tiled[i] = (i & 0xFF);
}
ail_detile(tiled, linear, &layout, 0, linear_stride, rx, ry, rw, rh);
ref_access_tiled(tiled, ref, &layout, rx, ry, rw, rh, linear_stride,
false);
EXPECT_EQ(memcmp(ref, linear, bpp * rh * linear_stride), 0);
}
free(ref);
free(tiled);
free(linear);
}
static void
test_ldst(unsigned width, unsigned height, unsigned rx, unsigned ry,
unsigned rw, unsigned rh, unsigned linear_stride,
enum pipe_format format)
{
test(width, height, rx, ry, rw, rh, linear_stride, format, true);
test(width, height, rx, ry, rw, rh, linear_stride, format, false);
}
TEST(Twiddling, RegularFormats)
{
test_ldst(233, 173, 0, 0, 233, 173, 233, PIPE_FORMAT_R8_UINT);
test_ldst(233, 173, 0, 0, 233, 173, 233 * 2, PIPE_FORMAT_R8G8_UINT);
test_ldst(233, 173, 0, 0, 233, 173, 233 * 4, PIPE_FORMAT_R32_UINT);
test_ldst(173, 143, 0, 0, 173, 143, 173 * 8, PIPE_FORMAT_R32G32_UINT);
test_ldst(133, 143, 0, 0, 133, 143, 133 * 16, PIPE_FORMAT_R32G32B32A32_UINT);
}
TEST(Twiddling, UnpackedStrides)
{
test_ldst(213, 17, 0, 0, 213, 17, 369 * 1, PIPE_FORMAT_R8_SINT);
test_ldst(213, 17, 0, 0, 213, 17, 369 * 2, PIPE_FORMAT_R8G8_SINT);
test_ldst(213, 17, 0, 0, 213, 17, 369 * 4, PIPE_FORMAT_R32_SINT);
test_ldst(213, 17, 0, 0, 213, 17, 369 * 8, PIPE_FORMAT_R32G32_SINT);
test_ldst(213, 17, 0, 0, 213, 17, 369 * 16, PIPE_FORMAT_R32G32B32A32_SINT);
}
TEST(Twiddling, PartialAccess)
{
test_ldst(283, 171, 3, 1, 131, 7, 369 * 1, PIPE_FORMAT_R8_UNORM);
test_ldst(283, 171, 3, 1, 131, 7, 369 * 2, PIPE_FORMAT_R8G8_UNORM);
test_ldst(283, 171, 3, 1, 131, 7, 369 * 4, PIPE_FORMAT_R32_UNORM);
test_ldst(283, 171, 3, 1, 131, 7, 369 * 8, PIPE_FORMAT_R32G32_UNORM);
test_ldst(283, 171, 3, 1, 131, 7, 369 * 16, PIPE_FORMAT_R32G32B32A32_UNORM);
}
TEST(Twiddling, ETC)
{
/* Block alignment assumed */
test_ldst(32, 32, 0, 0, 32, 32, 512, PIPE_FORMAT_ETC1_RGB8);
test_ldst(32, 256, 0, 0, 32, 256, 512, PIPE_FORMAT_ETC2_RGB8A1);
test_ldst(32, 512, 0, 0, 32, 512, 512, PIPE_FORMAT_ETC2_RG11_SNORM);
}
TEST(Twiddling, PartialETC)
{
/* Block alignment assumed */
test_ldst(32, 32, 4, 8, 16, 12, 512, PIPE_FORMAT_ETC1_RGB8);
test_ldst(32, 32, 4, 8, 16, 12, 512, PIPE_FORMAT_ETC2_RGB8A1);
test_ldst(32, 32, 4, 8, 16, 12, 512, PIPE_FORMAT_ETC2_RG11_SNORM);
}
TEST(Twiddling, DXT)
{
/* Block alignment assumed */
test_ldst(32, 32, 0, 0, 32, 32, 512, PIPE_FORMAT_DXT1_RGB);
test_ldst(32, 32, 0, 0, 32, 32, 512, PIPE_FORMAT_DXT3_RGBA);
test_ldst(32, 32, 0, 0, 32, 32, 512, PIPE_FORMAT_DXT5_RGBA);
}
TEST(Twiddling, PartialDXT)
{
/* Block alignment assumed */
test_ldst(32, 32, 4, 8, 16, 12, 512, PIPE_FORMAT_DXT1_RGB);
test_ldst(32, 32, 4, 8, 16, 12, 512, PIPE_FORMAT_DXT3_RGBA);
test_ldst(32, 32, 4, 8, 16, 12, 512, PIPE_FORMAT_DXT5_RGBA);
}
TEST(Twiddling, ASTC)
{
/* Block alignment assumed */
test_ldst(40, 40, 0, 0, 40, 40, 512, PIPE_FORMAT_ASTC_4x4);
test_ldst(50, 40, 0, 0, 50, 40, 512, PIPE_FORMAT_ASTC_5x4);
test_ldst(50, 50, 0, 0, 50, 50, 512, PIPE_FORMAT_ASTC_5x5);
}
TEST(Twiddling, PartialASTC)
{
/* Block alignment assumed */
test_ldst(40, 40, 4, 4, 16, 8, 512, PIPE_FORMAT_ASTC_4x4);
test_ldst(50, 40, 5, 4, 10, 8, 512, PIPE_FORMAT_ASTC_5x4);
test_ldst(50, 50, 5, 5, 10, 10, 512, PIPE_FORMAT_ASTC_5x5);
}