blob: cf4742950837ad58066e160e1659c0b7d792d337 [file] [log] [blame]
/*
* Copyright (C) 2022 Collabora, Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "pan_afbc.h"
#include "pan_afrc.h"
#include "pan_format.h"
#include "pan_image.h"
#include "pan_layout.h"
#include "pan_mod.h"
#include <gtest/gtest.h>
TEST(Align, UTiledLinear)
{
struct {
unsigned arch;
enum pipe_format format;
unsigned plane_idx;
unsigned alignment;
} cases[] = {
{ 6, PIPE_FORMAT_ETC2_RGB8, 0, 8 },
{ 6, PIPE_FORMAT_R32G32B32_FLOAT, 0, 4 },
{ 6, PIPE_FORMAT_R8G8B8A8_UNORM, 0, 1 },
{ 6, PIPE_FORMAT_R5G6B5_UNORM, 0, 2 },
{ 6, PIPE_FORMAT_R8_G8B8_420_UNORM, 0, 1 },
{ 6, PIPE_FORMAT_R8_G8B8_420_UNORM, 1, 2 },
{ 7, PIPE_FORMAT_ETC2_RGB8, 0, 64 },
{ 7, PIPE_FORMAT_R32G32B32_FLOAT, 0, 64 },
{ 7, PIPE_FORMAT_R8G8B8A8_UNORM, 0, 64 },
{ 7, PIPE_FORMAT_R5G6B5_UNORM, 0, 64 },
{ 7, PIPE_FORMAT_R8_G8B8_420_UNORM, 0, 16 },
{ 7, PIPE_FORMAT_R8_G8B8_420_UNORM, 1, 16 },
{ 7, PIPE_FORMAT_R10_G10B10_420_UNORM, 0, 1 },
{ 7, PIPE_FORMAT_R10_G10B10_420_UNORM, 1, 1 },
};
for (unsigned i = 0; i < ARRAY_SIZE(cases); ++i) {
unsigned align = pan_linear_or_tiled_row_align_req(
cases[i].arch, cases[i].format, cases[i].plane_idx);
EXPECT_EQ(align, cases[i].alignment);
}
}
TEST(BlockSize, UInterleavedRegular)
{
enum pipe_format format[] = {
PIPE_FORMAT_R32G32B32_FLOAT,
PIPE_FORMAT_R8G8B8_UNORM,
};
for (unsigned i = 0; i < ARRAY_SIZE(format); ++i) {
struct pan_image_block_size blk =
pan_u_interleaved_tile_size_el(format[i]);
EXPECT_EQ(blk.width, 16);
EXPECT_EQ(blk.height, 16);
}
}
TEST(BlockSize, UInterleavedBlockCompressed)
{
enum pipe_format format[] = {PIPE_FORMAT_ETC2_RGB8, PIPE_FORMAT_ASTC_5x5};
for (unsigned i = 0; i < ARRAY_SIZE(format); ++i) {
struct pan_image_block_size blk =
pan_u_interleaved_tile_size_el(format[i]);
EXPECT_EQ(blk.width, 4);
EXPECT_EQ(blk.height, 4);
}
}
TEST(BlockSize, AFBCFormatInvariant16x16)
{
enum pipe_format format[] = {PIPE_FORMAT_R32G32B32_FLOAT,
PIPE_FORMAT_R8G8B8_UNORM};
uint64_t modifier =
DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
AFBC_FORMAT_MOD_SPARSE | AFBC_FORMAT_MOD_YTR);
for (unsigned i = 0; i < ARRAY_SIZE(format); ++i) {
struct pan_image_block_size blk =
pan_afbc_superblock_size_el(format[i], modifier);
EXPECT_EQ(blk.width, 16);
EXPECT_EQ(blk.height, 16);
}
}
TEST(BlockSize, AFBCFormatInvariant32x8)
{
enum pipe_format format[] = {PIPE_FORMAT_R32G32B32_FLOAT,
PIPE_FORMAT_R8G8B8_UNORM};
uint64_t modifier =
DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 |
AFBC_FORMAT_MOD_SPARSE | AFBC_FORMAT_MOD_YTR);
for (unsigned i = 0; i < ARRAY_SIZE(format); ++i) {
struct pan_image_block_size blk =
pan_afbc_superblock_size_el(format[i], modifier);
EXPECT_EQ(blk.width, 32);
EXPECT_EQ(blk.height, 8);
}
}
TEST(BlockSize, AFBCSuperblock16x16)
{
uint64_t modifier =
DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
AFBC_FORMAT_MOD_SPARSE | AFBC_FORMAT_MOD_YTR);
EXPECT_EQ(pan_afbc_superblock_size(modifier).width, 16);
EXPECT_EQ(pan_afbc_superblock_width(modifier), 16);
EXPECT_EQ(pan_afbc_superblock_size(modifier).height, 16);
EXPECT_EQ(pan_afbc_superblock_height(modifier), 16);
EXPECT_FALSE(pan_afbc_is_wide(modifier));
}
TEST(BlockSize, AFBCSuperblock32x8)
{
uint64_t modifier = DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 |
AFBC_FORMAT_MOD_SPARSE);
EXPECT_EQ(pan_afbc_superblock_size(modifier).width, 32);
EXPECT_EQ(pan_afbc_superblock_width(modifier), 32);
EXPECT_EQ(pan_afbc_superblock_size(modifier).height, 8);
EXPECT_EQ(pan_afbc_superblock_height(modifier), 8);
EXPECT_TRUE(pan_afbc_is_wide(modifier));
}
TEST(BlockSize, AFBCSuperblock64x4)
{
uint64_t modifier = DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_64x4 |
AFBC_FORMAT_MOD_SPARSE);
EXPECT_EQ(pan_afbc_superblock_size(modifier).width, 64);
EXPECT_EQ(pan_afbc_superblock_width(modifier), 64);
EXPECT_EQ(pan_afbc_superblock_size(modifier).height, 4);
EXPECT_EQ(pan_afbc_superblock_height(modifier), 4);
EXPECT_TRUE(pan_afbc_is_wide(modifier));
}
/* Calculate Bifrost line stride, since we have reference formulas for Bifrost
* stride calculations.
*/
static uint32_t
pan_afbc_line_stride(uint64_t modifier, uint32_t width)
{
return pan_afbc_stride_blocks(modifier,
pan_afbc_row_stride(modifier, width));
}
/* Which form of the stride we specify is hardware specific (row stride for
* Valhall, line stride for Bifrost). However, the layout code is hardware
* independent, so we test both row stride and line stride calculations.
*/
TEST(AFBCStride, Linear)
{
uint64_t modifiers[] = {
DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
AFBC_FORMAT_MOD_SPARSE),
DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 |
AFBC_FORMAT_MOD_SPARSE),
DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_64x4 |
AFBC_FORMAT_MOD_SPARSE),
};
for (unsigned m = 0; m < ARRAY_SIZE(modifiers); ++m) {
uint64_t modifier = modifiers[m];
uint32_t sw = pan_afbc_superblock_width(modifier);
uint32_t cases[] = {1, 4, 17, 39};
for (unsigned i = 0; i < ARRAY_SIZE(cases); ++i) {
uint32_t width = sw * cases[i];
EXPECT_EQ(pan_afbc_row_stride(modifier, width),
16 * DIV_ROUND_UP(width, sw));
EXPECT_EQ(pan_afbc_line_stride(modifier, width),
DIV_ROUND_UP(width, sw));
}
}
}
TEST(AFBCStride, Tiled)
{
uint64_t modifiers[] = {
DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
AFBC_FORMAT_MOD_TILED | AFBC_FORMAT_MOD_SPARSE),
DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 |
AFBC_FORMAT_MOD_TILED | AFBC_FORMAT_MOD_SPARSE),
DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_64x4 |
AFBC_FORMAT_MOD_TILED | AFBC_FORMAT_MOD_SPARSE),
};
for (unsigned m = 0; m < ARRAY_SIZE(modifiers); ++m) {
uint64_t modifier = modifiers[m];
uint32_t sw = pan_afbc_superblock_width(modifier);
uint32_t cases[] = {1, 4, 17, 39};
for (unsigned i = 0; i < ARRAY_SIZE(cases); ++i) {
uint32_t width = sw * 8 * cases[i];
EXPECT_EQ(pan_afbc_row_stride(modifier, width),
16 * DIV_ROUND_UP(width, (sw * 8)) * 8 * 8);
EXPECT_EQ(pan_afbc_line_stride(modifier, width),
DIV_ROUND_UP(width, sw * 8) * 8);
}
}
}
static bool
layout_init(unsigned arch, const struct pan_image_props *props,
unsigned plane_idx,
const struct pan_image_layout_constraints *layout_constraints,
struct pan_image_layout *layout)
{
/* Pick the first supported arch if it's zero. */
if (!arch)
arch = 4;
struct pan_image_plane plane = {
};
struct pan_image img = {
.props = *props,
.mod_handler = pan_mod_get_handler(arch, props->modifier),
};
img.planes[plane_idx] = &plane;
if (!pan_image_layout_init(arch, &img, plane_idx, layout_constraints))
return false;
*layout = plane.layout;
return true;
}
/* dEQP-GLES3.functional.texture.format.compressed.etc1_2d_pot */
TEST(Layout, ImplicitLayoutInterleavedETC2)
{
struct pan_image_props p = {
.modifier = DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED,
.format = PIPE_FORMAT_ETC2_RGB8,
.extent_px = {
.width = 128,
.height = 128,
.depth = 1,
},
.nr_samples = 1,
.dim = MALI_TEXTURE_DIMENSION_2D,
.nr_slices = 8,
};
struct pan_image_layout l = {};
unsigned offsets[9] = {0, 8192, 10240, 10752, 10880,
11008, 11136, 11264, 11392};
ASSERT_TRUE(layout_init(0, &p, 0, NULL, &l));
for (unsigned i = 0; i < 8; ++i) {
unsigned size = (offsets[i + 1] - offsets[i]);
EXPECT_EQ(l.slices[i].offset_B, offsets[i]);
if (size == 64)
EXPECT_TRUE(l.slices[i].size_B < 64);
else
EXPECT_EQ(l.slices[i].size_B, size);
}
}
TEST(Layout, ImplicitLayoutInterleavedASTC5x5)
{
struct pan_image_props p = {
.modifier = DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED,
.format = PIPE_FORMAT_ASTC_5x5,
.extent_px = {
.width = 50,
.height = 50,
.depth = 1,
},
.nr_samples = 1,
.dim = MALI_TEXTURE_DIMENSION_2D,
.nr_slices = 1,
};
struct pan_image_layout l = {};
ASSERT_TRUE(layout_init(0, &p, 0, NULL, &l));
/* The image is 50x50 pixels, with 5x5 blocks. So it is a 10x10 grid of ASTC
* blocks. 4x4 tiles of ASTC blocks are u-interleaved, so we have to round up
* to a 12x12 grid. So we need space for 144 ASTC blocks. Each ASTC block is
* 16 bytes (128-bits), so we require 2304 bytes, with a row stride of 12 *
* 16 * 4 = 192 bytes.
*/
EXPECT_EQ(l.slices[0].offset_B, 0);
EXPECT_EQ(l.slices[0].tiled_or_linear.row_stride_B, 768);
EXPECT_EQ(l.slices[0].tiled_or_linear.surface_stride_B, 2304);
EXPECT_EQ(l.slices[0].size_B, 2304);
}
TEST(Layout, ImplicitLayoutLinearASTC5x5)
{
struct pan_image_props p = {
.modifier = DRM_FORMAT_MOD_LINEAR,
.format = PIPE_FORMAT_ASTC_5x5,
.extent_px = {
.width = 50,
.height = 50,
.depth = 1,
},
.nr_samples = 1,
.dim = MALI_TEXTURE_DIMENSION_2D,
.nr_slices = 1,
};
struct pan_image_layout l = {};
ASSERT_TRUE(layout_init(0, &p, 0, NULL, &l));
/* The image is 50x50 pixels, with 5x5 blocks. So it is a 10x10 grid of ASTC
* blocks. Each ASTC block is 16 bytes, so the row stride is 160 bytes,
* rounded up to the cache line (192 bytes). There are 10 rows, so we have
* 1920 bytes total.
*/
EXPECT_EQ(l.slices[0].offset_B, 0);
EXPECT_EQ(l.slices[0].tiled_or_linear.row_stride_B, 192);
EXPECT_EQ(l.slices[0].tiled_or_linear.surface_stride_B, 1920);
EXPECT_EQ(l.slices[0].size_B, 1920);
}
/* dEQP-GLES3.functional.texture.format.unsized.rgba_unsigned_byte_3d_pot */
TEST(AFBCLayout, Linear3D)
{
uint64_t modifier = DRM_FORMAT_MOD_ARM_AFBC(
AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | AFBC_FORMAT_MOD_SPARSE);
struct pan_image_props p = {
.modifier = modifier,
.format = PIPE_FORMAT_R8G8B8A8_UNORM,
.extent_px = {
.width = 8,
.height = 32,
.depth = 16,
},
.nr_samples = 1,
.dim = MALI_TEXTURE_DIMENSION_3D,
.nr_slices = 1,
};
struct pan_image_layout l = {};
ASSERT_TRUE(layout_init(0, &p, 0, NULL, &l));
/* AFBC Surface size is the size of headers for a single surface. At superblock
* size 16x16, the 8x32 layer has 1x2 superblocks, so the header size is 2 *
* 16 = 32 bytes. Body offset needs to be aligned on 64 bytes on v6-.
* Header/body sections of a 3D image are interleaved, so the surface stride is
* is the header size, aligned to meet body offset alignment constraints, plus
* the body of a single surface.
*
* There is only 1 superblock per row, so the row stride is the bytes per 1
* header block = 16.
*
* There are 16 layers of size 64 so afbc.header_size = 16 * 64 = 1024.
*
* Each 16x16 superblock consumes 16 * 16 * 4 = 1024 bytes. There are 2 * 1 *
* 16 superblocks in the image, so body size is 32768.
*/
EXPECT_EQ(l.slices[0].offset_B, 0);
EXPECT_EQ(l.slices[0].afbc.header.row_stride_B, 16);
EXPECT_EQ(l.slices[0].afbc.header.surface_size_B, 32);
EXPECT_EQ(l.slices[0].afbc.surface_stride_B, 64 + 2048);
EXPECT_EQ(l.slices[0].size_B, (64 + 2048) * 16);
}
TEST(AFBCLayout, Tiled16x16)
{
uint64_t modifier =
DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
AFBC_FORMAT_MOD_TILED | AFBC_FORMAT_MOD_SPARSE);
struct pan_image_props p = {
.modifier = modifier,
.format = PIPE_FORMAT_R8G8B8A8_UNORM,
.extent_px = {
.width = 917,
.height = 417,
.depth = 1,
},
.nr_samples = 1,
.dim = MALI_TEXTURE_DIMENSION_2D,
.nr_slices = 1,
};
struct pan_image_layout l = {};
ASSERT_TRUE(layout_init(0, &p, 0, NULL, &l));
/* The image is 917x417. Superblocks are 16x16, so there are 58x27
* superblocks. Superblocks are grouped into 8x8 tiles, so there are 8x4
* tiles of superblocks. So the row stride is 16 * 8 * 8 * 8 = 8192 bytes.
* There are 4 tiles vertically, so the header is 8192 * 4 = 32768 bytes.
* This is already 4096-byte aligned.
*
* Each tile of superblock contains 128x128 pixels and each pixel is 4 bytes,
* so tiles are 65536 bytes, meaning the payload is 8 * 4 * 65536 = 2097152
* bytes.
*
* In total, the AFBC surface is 32768 + 2097152 = 2129920 bytes.
*/
EXPECT_EQ(l.slices[0].offset_B, 0);
EXPECT_EQ(l.slices[0].afbc.header.row_stride_B, 8192);
EXPECT_EQ(l.slices[0].afbc.header.surface_size_B, 32768);
EXPECT_EQ(l.slices[0].afbc.surface_stride_B, 2129920);
EXPECT_EQ(l.slices[0].size_B, 2129920);
}
TEST(AFBCLayout, Linear16x16Minimal)
{
uint64_t modifier = DRM_FORMAT_MOD_ARM_AFBC(
AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | AFBC_FORMAT_MOD_SPARSE);
struct pan_image_props p = {
.modifier = modifier,
.format = PIPE_FORMAT_R8_UNORM,
.extent_px = {
.width = 1,
.height = 1,
.depth = 1,
},
.nr_samples = 1,
.dim = MALI_TEXTURE_DIMENSION_2D,
.nr_slices = 1,
};
struct pan_image_layout l = {};
ASSERT_TRUE(layout_init(0, &p, 0, NULL, &l));
/* Image is 1x1 to test for correct alignment everywhere. */
EXPECT_EQ(l.slices[0].offset_B, 0);
EXPECT_EQ(l.slices[0].afbc.header.row_stride_B, 16);
EXPECT_EQ(l.slices[0].afbc.header.surface_size_B, 16);
EXPECT_EQ(l.slices[0].afbc.surface_stride_B, 64 + (32 * 8));
EXPECT_EQ(l.slices[0].size_B, 64 + (32 * 8));
}
TEST(AFBCLayout, Linear16x16Minimalv6)
{
uint64_t modifier = DRM_FORMAT_MOD_ARM_AFBC(
AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | AFBC_FORMAT_MOD_SPARSE);
struct pan_image_props p = {
.modifier = modifier,
.format = PIPE_FORMAT_R8_UNORM,
.extent_px = {
.width = 1,
.height = 1,
.depth = 1,
},
.nr_samples = 1,
.dim = MALI_TEXTURE_DIMENSION_2D,
.nr_slices = 1,
};
struct pan_image_layout l = {};
ASSERT_TRUE(layout_init(6, &p, 0, NULL, &l));
/* Image is 1x1 to test for correct alignment everywhere. */
EXPECT_EQ(l.slices[0].offset_B, 0);
EXPECT_EQ(l.slices[0].afbc.header.row_stride_B, 16);
EXPECT_EQ(l.slices[0].afbc.header.surface_size_B, 16);
EXPECT_EQ(l.slices[0].afbc.surface_stride_B, 128 + (32 * 8));
EXPECT_EQ(l.slices[0].size_B, 128 + (32 * 8));
}
TEST(AFBCLayout, Tiled16x16Minimal)
{
uint64_t modifier =
DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
AFBC_FORMAT_MOD_TILED | AFBC_FORMAT_MOD_SPARSE);
struct pan_image_props p = {
.modifier = modifier,
.format = PIPE_FORMAT_R8_UNORM,
.extent_px = {
.width = 1,
.height = 1,
.depth = 1,
},
.nr_samples = 1,
.dim = MALI_TEXTURE_DIMENSION_2D,
.nr_slices = 1,
};
struct pan_image_layout l = {};
ASSERT_TRUE(layout_init(0, &p, 0, NULL, &l));
/* Image is 1x1 to test for correct alignment everywhere. */
EXPECT_EQ(l.slices[0].offset_B, 0);
EXPECT_EQ(l.slices[0].afbc.header.row_stride_B, 16 * 8 * 8);
EXPECT_EQ(l.slices[0].afbc.header.surface_size_B, 16 * 8 * 8);
EXPECT_EQ(l.slices[0].afbc.surface_stride_B, 4096 + (32 * 8 * 8 * 8));
EXPECT_EQ(l.slices[0].size_B, 4096 + (32 * 8 * 8 * 8));
}
static unsigned archs[] = {4, 5, 6, 7, 9, 12, 13};
#define IMAGE_WIDTH 4096
#define IMAGE_HEIGHT 512
#define IMAGE_FORMAT \
(PAN_BIND_DEPTH_STENCIL | PAN_BIND_RENDER_TARGET | PAN_BIND_SAMPLER_VIEW | \
PAN_BIND_STORAGE_IMAGE)
#define EXPECT_IMPORT_SUCCESS(__arch, __iprops, __plane, __wsi_layout, \
__out_layout, __test_desc) \
do { \
bool __result = \
layout_init(__arch, __iprops, __plane, __wsi_layout, __out_layout); \
EXPECT_TRUE(__result) \
<< __test_desc \
<< " for <format=" << util_format_name((__iprops)->format) \
<< ",plane=" << __plane << ",mod=" << std::hex \
<< (__iprops)->modifier << std::dec << "> rejected (arch=" << __arch \
<< ")"; \
\
if (!__result) \
break; \
\
struct pan_image_plane img_plane = { \
.layout = *(__out_layout), \
}; \
struct pan_image img = { \
.props = *(__iprops), \
.mod_handler = pan_mod_get_handler(__arch, (__iprops)->modifier), \
}; \
img.planes[__plane] = &img_plane; \
unsigned __export_row_pitch_B = \
pan_image_get_wsi_row_pitch(&img, __plane, 0); \
unsigned __export_offset_B = pan_image_get_wsi_offset(&img, __plane, 0); \
EXPECT_TRUE(__export_row_pitch_B == (__wsi_layout)->wsi_row_pitch_B && \
__export_offset_B == (__wsi_layout)->offset_B) \
<< " mismatch between import and export for <format=" \
<< util_format_name(iprops.format) << ",plane=" << __plane \
<< ",mod=" << std::hex << (__iprops)->modifier << std::dec \
<< "> (arch=" << __arch << ")"; \
} while (0)
#define EXPECT_IMPORT_FAIL(__arch, __iprops, __plane, __wsi_layout, \
__out_layout, __test_desc) \
EXPECT_FALSE( \
layout_init(__arch, __iprops, __plane, __wsi_layout, __out_layout)) \
<< __test_desc \
<< " for <format=" << util_format_name((__iprops)->format) \
<< ",plane=" << __plane << ",mod=" << std::hex << (__iprops)->modifier \
<< std::dec << "> not rejected (arch=" << __arch << ")"
static bool
format_can_do_mod(unsigned arch, enum pipe_format format, unsigned plane_idx,
uint64_t modifier)
{
if (drm_is_afbc(modifier)) {
return pan_afbc_format(arch, format, plane_idx) != PAN_AFBC_MODE_INVALID;
} else if (drm_is_afrc(modifier)) {
return arch >= 10 && pan_afrc_supports_format(format);
} else {
assert(modifier == DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED ||
modifier == DRM_FORMAT_MOD_LINEAR);
switch (format) {
case PIPE_FORMAT_R8G8B8_420_UNORM_PACKED:
case PIPE_FORMAT_R10G10B10_420_UNORM_PACKED:
/* Those are only supported with AFBC. */
return false;
default:
return true;
}
}
}
static unsigned
offset_align_for_mod(unsigned arch, const struct pan_image_props *iprops,
unsigned plane_idx)
{
uint64_t modifier = iprops->modifier;
enum pipe_format format = iprops->format;
if (drm_is_afbc(modifier)) {
return pan_afbc_header_align(arch, modifier);
} else if (drm_is_afrc(modifier)) {
return pan_afrc_buffer_alignment_from_modifier(modifier);
} else {
assert(modifier == DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED ||
modifier == DRM_FORMAT_MOD_LINEAR);
return pan_linear_or_tiled_row_align_req(arch, format, plane_idx);
}
}
static unsigned
row_align_for_mod(unsigned arch, const struct pan_image_props *iprops,
unsigned plane_idx)
{
uint64_t modifier = iprops->modifier;
enum pipe_format format = iprops->format;
if (drm_is_afbc(modifier)) {
unsigned hdr_row_align =
pan_afbc_header_row_stride_align(arch, format, modifier);
unsigned ntiles = hdr_row_align / AFBC_HEADER_BYTES_PER_TILE;
unsigned sb_width_el = pan_afbc_superblock_width(modifier) /
util_format_get_blockwidth(format);
assert(pan_afbc_superblock_width(modifier) %
util_format_get_blockwidth(format) ==
0);
return ntiles * sb_width_el *
pan_format_get_plane_blocksize(format, plane_idx);
} else if (drm_is_afrc(modifier)) {
unsigned row_align = pan_afrc_buffer_alignment_from_modifier(modifier);
struct pan_image_block_size tile_size_px =
pan_afrc_tile_size(format, modifier);
assert(row_align % tile_size_px.height == 0);
return row_align / tile_size_px.height;
} else {
assert(modifier == DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED ||
modifier == DRM_FORMAT_MOD_LINEAR);
unsigned tile_height_el = modifier == DRM_FORMAT_MOD_LINEAR ? 1
: util_format_is_compressed(format) ? 4
: 16;
return DIV_ROUND_UP(offset_align_for_mod(arch, iprops, plane_idx),
tile_height_el);
}
}
static unsigned
default_wsi_row_pitch(unsigned arch, const struct pan_image_props *iprops,
unsigned plane_idx)
{
uint64_t modifier = iprops->modifier;
enum pipe_format format = iprops->format;
unsigned fmt_blksz_B = pan_format_get_plane_blocksize(format, plane_idx);
unsigned width_px =
util_format_get_plane_width(format, plane_idx, iprops->extent_px.width);
assert(width_px % util_format_get_blockwidth(format) == 0);
if (drm_is_afbc(modifier)) {
unsigned sb_width_el = pan_afbc_superblock_width(modifier) /
util_format_get_blockwidth(format);
unsigned sb_height_el = pan_afbc_superblock_height(modifier) /
util_format_get_blockheight(format);
unsigned ntiles =
DIV_ROUND_UP(width_px, pan_afbc_superblock_width(modifier));
unsigned tile_row_size_B =
sb_width_el * sb_height_el * fmt_blksz_B * ntiles;
assert(pan_afbc_superblock_width(modifier) %
util_format_get_blockwidth(format) ==
0);
assert(pan_afbc_superblock_height(modifier) %
util_format_get_blockheight(format) ==
0);
assert(tile_row_size_B % pan_afbc_superblock_height(modifier) == 0);
return tile_row_size_B / pan_afbc_superblock_height(modifier);
} else if (drm_is_afrc(modifier)) {
struct pan_image_block_size tile_size =
pan_afrc_tile_size(format, modifier);
unsigned afrc_row_stride_B =
pan_afrc_row_stride(format, modifier, width_px);
assert(afrc_row_stride_B % tile_size.height == 0);
return afrc_row_stride_B / tile_size.height;
} else {
assert(modifier == DRM_FORMAT_MOD_LINEAR ||
modifier == DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED);
unsigned row_pitch_B =
(width_px / util_format_get_blockwidth(format)) * fmt_blksz_B;
struct pan_image_block_size tile_size_el = {1, 1};
if (modifier == DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED) {
if (util_format_is_compressed(format)) {
tile_size_el.width = 4;
tile_size_el.height = 4;
} else {
tile_size_el.width = 16;
tile_size_el.height = 16;
}
}
assert(width_px %
(util_format_get_blockwidth(format) * tile_size_el.width) ==
0);
return row_pitch_B;
}
}
TEST(WSI, Import)
{
/* We don't want to spam stderr with failure messages caused by our
* EXPECT_FALSE() cases. */
setenv("MESA_LOG", "null", 0);
struct pan_image_layout layout;
for (unsigned i = 0; i < ARRAY_SIZE(archs); i++) {
unsigned arch = archs[i];
const struct pan_format *ftable = pan_format_table(arch);
PAN_SUPPORTED_MODIFIERS(mods);
for (unsigned m = 0; m < ARRAY_SIZE(mods); m++) {
for (unsigned fmt = PIPE_FORMAT_NONE + 1; fmt < PIPE_FORMAT_COUNT;
fmt++) {
if (!(ftable[fmt].bind & IMAGE_FORMAT))
continue;
struct pan_image_props iprops = {
.modifier = mods[m],
.format = (enum pipe_format)fmt,
.extent_px = {
.width = IMAGE_WIDTH,
.height = IMAGE_HEIGHT,
.depth = 1,
},
.nr_samples = 1,
.dim = MALI_TEXTURE_DIMENSION_2D,
.nr_slices = 1,
.array_size = 1,
.crc = false,
};
bool supported = true;
for (unsigned p = 0; p < util_format_get_num_planes(iprops.format);
p++) {
if (!format_can_do_mod(arch, iprops.format, p,
iprops.modifier)) {
supported = false;
break;
}
}
if (!supported)
continue;
if (util_format_is_compressed(iprops.format)) {
/* We multiply the image extent by the block extent to make sure
* things are always aligned on a block. */
iprops.extent_px.width *=
util_format_get_blockwidth(iprops.format);
iprops.extent_px.height *=
util_format_get_blockheight(iprops.format);
}
for (unsigned p = 0; p < util_format_get_num_planes(iprops.format);
p++) {
unsigned row_align_req_B =
row_align_for_mod(arch, &iprops, p);
unsigned offset_align_req_B =
offset_align_for_mod(arch, &iprops, p);
unsigned default_row_pitch_B =
default_wsi_row_pitch(arch, &iprops, p);
assert(default_row_pitch_B > row_align_req_B);
if (row_align_req_B > 1) {
struct pan_image_layout_constraints wsi_layout = {
.wsi_row_pitch_B = default_row_pitch_B + 1,
.strict = true,
};
EXPECT_IMPORT_FAIL(arch, &iprops, p, &wsi_layout, &layout,
"unaligned WSI row pitch");
}
if (offset_align_req_B > 1) {
struct pan_image_layout_constraints wsi_layout = {
.offset_B = 1,
.wsi_row_pitch_B = default_row_pitch_B,
.strict = true,
};
EXPECT_IMPORT_FAIL(arch, &iprops, p, &wsi_layout, &layout,
"unaligned WSI offset");
}
/* Exact match. */
struct pan_image_layout_constraints wsi_layout = {
.wsi_row_pitch_B = default_row_pitch_B,
.strict = true,
};
EXPECT_IMPORT_SUCCESS(arch, &iprops, p, &wsi_layout, &layout,
"tightly packed lines");
wsi_layout.wsi_row_pitch_B =
default_row_pitch_B + row_align_req_B;
EXPECT_IMPORT_SUCCESS(arch, &iprops, p, &wsi_layout, &layout,
"lines with padding");
wsi_layout.wsi_row_pitch_B =
default_row_pitch_B - row_align_req_B;
EXPECT_IMPORT_FAIL(arch, &iprops, p, &wsi_layout, &layout,
"partially aliased lines");
wsi_layout.wsi_row_pitch_B = default_row_pitch_B;
wsi_layout.offset_B = offset_align_req_B;
EXPECT_IMPORT_SUCCESS(arch, &iprops, p, &wsi_layout, &layout,
"properly aligned offset");
}
}
}
}
}