From 66a680a0435f5d1a7ba937fcd11aeba3581bbee8 Mon Sep 17 00:00:00 2001 From: Alyssa Rosenzweig Date: Fri, 18 Nov 2022 22:31:58 -0500 Subject: [PATCH] asahi: Add tilebuffer layout helpers Laying out the tilebuffer is nontrivial and a task shared between GL and VK, so add unit-tested helpers. Signed-off-by: Alyssa Rosenzweig Part-of: --- src/asahi/lib/agx_tilebuffer.c | 110 +++++++++++++++++ src/asahi/lib/agx_tilebuffer.h | 64 ++++++++++ src/asahi/lib/meson.build | 6 +- src/asahi/lib/tests/test-tilebuffer.cpp | 158 ++++++++++++++++++++++++ 4 files changed, 336 insertions(+), 2 deletions(-) create mode 100644 src/asahi/lib/agx_tilebuffer.c create mode 100644 src/asahi/lib/agx_tilebuffer.h create mode 100644 src/asahi/lib/tests/test-tilebuffer.cpp diff --git a/src/asahi/lib/agx_tilebuffer.c b/src/asahi/lib/agx_tilebuffer.c new file mode 100644 index 00000000000..7b2d6f68370 --- /dev/null +++ b/src/asahi/lib/agx_tilebuffer.c @@ -0,0 +1,110 @@ +/* + * Copyright 2022 Alyssa Rosenzweig + * SPDX-License-Identifier: MIT + */ + +#include +#include "util/format/u_format.h" +#include "agx_tilebuffer.h" +#include "agx_formats.h" +#include "agx_usc.h" + +/* Maximum number of bytes per tile on G13G. This may change in future versions + * of the architecture. + */ +#define MAX_BYTES_PER_TILE (32768 - 1) + +/* Select the largest tile size that fits */ +static struct agx_tile_size +agx_select_tile_size(unsigned bytes_per_pixel) +{ + struct agx_tile_size sizes[] = { + { 32, 32 }, + { 32, 16 }, + { 16, 16 } + }; + + for (unsigned i = 0; i < ARRAY_SIZE(sizes); ++i) { + struct agx_tile_size size = sizes[i]; + + if ((bytes_per_pixel * size.width * size.height) <= MAX_BYTES_PER_TILE) + return size; + } + + unreachable("No supported tile size meets the bytes per pixel requirement"); +} + +struct agx_tilebuffer_layout +agx_build_tilebuffer_layout(enum pipe_format *formats, + uint8_t nr_cbufs, + uint8_t nr_samples) +{ + struct agx_tilebuffer_layout tib = { + .nr_samples = nr_samples + }; + + uint32_t offset_B = 0; + + for (unsigned rt = 0; rt < nr_cbufs; ++rt) { + tib.logical_format[rt] = formats[rt]; + + /* Require natural alignment for tilebuffer allocations. This could be + * optimized, but this shouldn't be a problem in practice. + */ + enum pipe_format physical_fmt = agx_tilebuffer_physical_format(&tib, rt); + unsigned align_B = util_format_get_blocksize(physical_fmt); + offset_B = ALIGN_POT(offset_B, align_B); + + tib.offset_B[rt] = offset_B; + + unsigned nr = util_format_get_nr_components(physical_fmt) == 1 ? + util_format_get_nr_components(formats[rt]) : 1; + + unsigned size_B = align_B * nr; + offset_B += size_B; + } + + assert(offset_B <= 128 && "should fit in uint8"); + tib.sample_size_B = ALIGN_POT(offset_B, 8); + + tib.tile_size = agx_select_tile_size(tib.sample_size_B * nr_samples); + return tib; +} + +enum pipe_format +agx_tilebuffer_physical_format(struct agx_tilebuffer_layout *tib, unsigned rt) +{ + return agx_pixel_format[tib->logical_format[rt]].internal; +} + +static unsigned +agx_shared_layout_from_tile_size(struct agx_tile_size t) +{ + if (t.width == 32 && t.height == 32) + return AGX_SHARED_LAYOUT_32X32; + else if (t.width == 32 && t.height == 16) + return AGX_SHARED_LAYOUT_32X16; + else if (t.width == 16 && t.height == 16) + return AGX_SHARED_LAYOUT_16X16; + else + unreachable("Invalid tile size"); +} + +uint32_t +agx_tilebuffer_total_size(struct agx_tilebuffer_layout *tib) +{ + return tib->sample_size_B * tib->nr_samples * + tib->tile_size.width * tib->tile_size.height; +} + +void +agx_usc_tilebuffer(struct agx_usc_builder *b, struct agx_tilebuffer_layout *tib) +{ + agx_usc_pack(b, SHARED, cfg) { + cfg.uses_shared_memory = true; + cfg.layout = agx_shared_layout_from_tile_size(tib->tile_size); + cfg.sample_stride_in_8_bytes = tib->sample_size_B / 8; + cfg.sample_count = tib->nr_samples; + cfg.bytes_per_threadgroup = agx_tilebuffer_total_size(tib); + } +} diff --git a/src/asahi/lib/agx_tilebuffer.h b/src/asahi/lib/agx_tilebuffer.h new file mode 100644 index 00000000000..b162ab0badd --- /dev/null +++ b/src/asahi/lib/agx_tilebuffer.h @@ -0,0 +1,64 @@ +/* + * Copyright 2022 Alyssa Rosenzweig + * SPDX-License-Identifier: MIT + */ + +#ifndef __AGX_TILEBUFFER_H +#define __AGX_TILEBUFFER_H + +#include +#include +#include "util/format/u_formats.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Forward declarations to keep the header lean */ +struct nir_shader; +struct agx_usc_builder; + +struct agx_tile_size { + uint8_t width; + uint8_t height; +}; + +struct agx_tilebuffer_layout { + /* Logical format of each render target. Use agx_tilebuffer_physical_format + * to get the physical format. + */ + enum pipe_format logical_format[8]; + + /* Offset into the sample of each render target */ + uint8_t offset_B[8]; + + /* Total bytes per sample, rounded up as needed */ + uint8_t sample_size_B; + + /* Number of samples per pixel */ + uint8_t nr_samples; + + /* Selected tile size */ + struct agx_tile_size tile_size; +}; + +struct agx_tilebuffer_layout +agx_build_tilebuffer_layout(enum pipe_format *formats, uint8_t nr_cbufs, uint8_t nr_samples); + +bool +agx_nir_lower_tilebuffer(struct nir_shader *shader, struct agx_tilebuffer_layout *tib); + +void +agx_usc_tilebuffer(struct agx_usc_builder *b, struct agx_tilebuffer_layout *tib); + +uint32_t +agx_tilebuffer_total_size(struct agx_tilebuffer_layout *tib); + +enum pipe_format +agx_tilebuffer_physical_format(struct agx_tilebuffer_layout *tib, unsigned rt); + +#ifdef __cplusplus +} /* extern C */ +#endif + +#endif diff --git a/src/asahi/lib/meson.build b/src/asahi/lib/meson.build index 14ba74f8106..abbb6d8bf98 100644 --- a/src/asahi/lib/meson.build +++ b/src/asahi/lib/meson.build @@ -24,6 +24,7 @@ dep_iokit = dependency('IOKit', required : false) libasahi_lib_files = files( 'agx_device.c', 'agx_formats.c', + 'agx_tilebuffer.c', 'agx_ppp.h', 'pool.c', ) @@ -74,12 +75,13 @@ if with_tests 'libasahi_tests', files( 'tests/test-packing.cpp', + 'tests/test-tilebuffer.cpp', ), c_args : [c_msvc_compat_args, no_override_init_args], gnu_symbol_visibility : 'hidden', include_directories : [inc_include, inc_src, inc_mesa], - dependencies: [idep_gtest, idep_agx_pack], - link_with : [], + dependencies: [idep_gtest, idep_agx_pack, idep_mesautil], + link_with : [libasahi_lib], ), suite : ['asahi'], protocol : gtest_test_protocol, diff --git a/src/asahi/lib/tests/test-tilebuffer.cpp b/src/asahi/lib/tests/test-tilebuffer.cpp new file mode 100644 index 00000000000..8f88dfae892 --- /dev/null +++ b/src/asahi/lib/tests/test-tilebuffer.cpp @@ -0,0 +1,158 @@ +/* + * Copyright 2022 Alyssa Rosenzweig + * SPDX-License-Identifier: MIT + */ + +#include "util/format/u_format.h" +#include "agx_tilebuffer.h" + +#include + +struct test { + const char *name; + uint8_t nr_samples; + enum pipe_format formats[8]; + struct agx_tilebuffer_layout layout; + uint32_t total_size; +}; + +struct test tests[] = { + { + "Simple test", + 1, + { PIPE_FORMAT_R8G8B8A8_UNORM }, + { + .offset_B = { 0 }, + .sample_size_B = 8, + .nr_samples = 1, + .tile_size = { 32, 32 }, + }, + 8192 + }, + { + "MSAA 2x", + 2, + { PIPE_FORMAT_R8G8B8A8_UNORM }, + { + .offset_B = { 0 }, + .sample_size_B = 8, + .nr_samples = 2, + .tile_size = { 32, 32 }, + }, + 16384 + }, + { + "MSAA 4x", + 4, + { PIPE_FORMAT_R8G8B8A8_UNORM }, + { + .offset_B = { 0 }, + .sample_size_B = 8, + .nr_samples = 4, + .tile_size = { 32, 16 }, + }, + 16384 + }, + { + "MRT", + 1, + { + PIPE_FORMAT_R16_SINT, + PIPE_FORMAT_R32G32_FLOAT, + PIPE_FORMAT_R8_SINT, + PIPE_FORMAT_R32G32_SINT, + }, + { + .offset_B = { 0, 4, 12, 16 }, + .sample_size_B = 24, + .nr_samples = 1, + .tile_size = { 32, 32 }, + }, + 24576 + }, + { + "MRT with MSAA 2x", + 2, + { + PIPE_FORMAT_R16_SINT, + PIPE_FORMAT_R32G32_FLOAT, + PIPE_FORMAT_R8_SINT, + PIPE_FORMAT_R32G32_SINT, + }, + { + .offset_B = { 0, 4, 12, 16 }, + .sample_size_B = 24, + .nr_samples = 2, + .tile_size = { 32, 16 }, + }, + 24576 + }, + { + "MRT with MSAA 4x", + 4, + { + PIPE_FORMAT_R16_SINT, + PIPE_FORMAT_R32G32_FLOAT, + PIPE_FORMAT_R8_SINT, + PIPE_FORMAT_R32G32_SINT, + }, + { + .offset_B = { 0, 4, 12, 16 }, + .sample_size_B = 24, + .nr_samples = 4, + .tile_size = { 16, 16 }, + }, + 24576 + }, + { + "MRT test requiring 2 alignment on the second RT", + 1, + { PIPE_FORMAT_R8_UNORM, PIPE_FORMAT_R16G16_SNORM }, + { + .offset_B = { 0, 2 }, + .sample_size_B = 8, + .nr_samples = 1, + .tile_size = { 32, 32 }, + }, + 8192 + }, + { + "Simple MRT test requiring 4 alignment on the second RT", + 1, + { PIPE_FORMAT_R8_UNORM, PIPE_FORMAT_R10G10B10A2_UNORM }, + { + .offset_B = { 0, 4 }, + .sample_size_B = 8, + .nr_samples = 1, + .tile_size = { 32, 32 }, + }, + 8192 + } +}; + +TEST(Tilebuffer, Layouts) +{ + for (unsigned i = 0; i < ARRAY_SIZE(tests); ++i) { + unsigned nr_cbufs; + + for (nr_cbufs = 0; + nr_cbufs < ARRAY_SIZE(tests[i].formats) && + tests[i].formats[nr_cbufs] != PIPE_FORMAT_NONE; + ++nr_cbufs); + + struct agx_tilebuffer_layout actual = + agx_build_tilebuffer_layout(tests[i].formats, nr_cbufs, + tests[i].nr_samples); + + ASSERT_EQ(tests[i].layout.sample_size_B, actual.sample_size_B) << + tests[i].name; + ASSERT_EQ(tests[i].layout.nr_samples, actual.nr_samples) << tests[i].name; + ASSERT_EQ(tests[i].layout.tile_size.width, actual.tile_size.width) << + tests[i].name; + ASSERT_EQ(tests[i].layout.tile_size.height, actual.tile_size.height) << + tests[i].name; + ASSERT_EQ(tests[i].total_size, + agx_tilebuffer_total_size(&tests[i].layout)) << + tests[i].name; + } +}