KytyPS5/src/graphics/guest_gpu/tile.cpp

3313 lines
121 KiB
C++

#include "graphics/guest_gpu/tile.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/profiler.h"
#include "common/stringUtils.h"
#include "common/threads.h"
#include "graphics/asyncJob.h"
#include "graphics/guest_gpu/gpu_defs.h"
#include "graphics/guest_gpu/gpu_format.h"
#include <algorithm>
#include <array>
#include <cstring>
#include <fmt/format.h>
#include <vector>
namespace Libs::Graphics {
static uint32_t IntLog2(uint32_t i) {
return 31 - __builtin_clz(i | 1u);
}
static uint32_t AlignUp(uint32_t value, uint32_t alignment) {
return (value + alignment - 1u) & ~(alignment - 1u);
}
static uint32_t ShiftCeil(uint32_t value, uint32_t shift) {
return static_cast<uint32_t>((static_cast<uint64_t>(value) + (1ull << shift) - 1ull) >> shift);
}
static uint32_t CalcLinearBlockWidth(uint32_t bytes_per_element) {
return std::max(1u, 256u / bytes_per_element);
}
static uint32_t CalcLinearAlignedLevelPitch(uint32_t base_width, uint32_t base_height,
uint32_t level, uint32_t bytes_per_element,
uint32_t* padded_height, uint32_t* level_size) {
const uint32_t level_width = ShiftCeil(base_width, level);
const uint32_t level_height = ShiftCeil(base_height, level);
const uint32_t padded_width =
AlignUp(std::max(level_width, 1u), CalcLinearBlockWidth(bytes_per_element));
const uint64_t size =
static_cast<uint64_t>(padded_width) * std::max(level_height, 1u) * bytes_per_element;
EXIT_NOT_IMPLEMENTED(size > 0xffffffffull);
*padded_height = std::max(level_height, 1u);
*level_size = static_cast<uint32_t>(size);
return padded_width;
}
static uint32_t SetLinearMipChainLayout(uint32_t levels, const uint32_t* mip_pitch,
const uint32_t* mip_height, const uint32_t* mip_size,
TileSizeOffset* level_sizes, TilePaddedSize* padded_size) {
uint32_t offset = 0;
// AGC linear surfaces store smaller mip records first; mip 0 is last in the block slice.
for (int32_t l = static_cast<int32_t>(levels) - 1; l >= 0; l--) {
const auto level = static_cast<uint32_t>(l);
if (level_sizes != nullptr) {
level_sizes[level].size = mip_size[level];
level_sizes[level].offset = offset;
}
if (padded_size != nullptr) {
padded_size[level].width = mip_pitch[level];
padded_size[level].height = mip_height[level];
}
offset += mip_size[level];
}
return AlignUp(offset, 256u);
}
static bool Gen5Standard4KBLayout(uint32_t format, uint32_t* bytes_per_element,
uint32_t* texels_per_element_wide,
uint32_t* texels_per_element_tall, uint32_t* block_width_log2,
uint32_t* block_height_log2) {
const auto bytes = Prospero::NumBytesPerElement(format);
switch (bytes) {
case 1:
*bytes_per_element = 1;
*texels_per_element_wide = 1;
*texels_per_element_tall = 1;
*block_width_log2 = 6;
*block_height_log2 = 6;
return true;
case 2:
*bytes_per_element = 2;
*texels_per_element_wide = 1;
*texels_per_element_tall = 1;
*block_width_log2 = 6;
*block_height_log2 = 5;
return true;
case 4:
*bytes_per_element = 4;
*texels_per_element_wide = 1;
*texels_per_element_tall = 1;
*block_width_log2 = 5;
*block_height_log2 = 5;
return true;
case 8:
*bytes_per_element = 8;
*texels_per_element_wide = 1;
*texels_per_element_tall = 1;
*block_width_log2 = 5;
*block_height_log2 = 4;
return true;
case 16:
*bytes_per_element = 16;
*texels_per_element_wide = 1;
*texels_per_element_tall = 1;
*block_width_log2 = 4;
*block_height_log2 = 4;
return true;
default: break;
}
switch (Prospero::BlockCompressedBytesPerBlock(format)) {
case 8:
*bytes_per_element = 8;
*texels_per_element_wide = 4;
*texels_per_element_tall = 4;
*block_width_log2 = 5;
*block_height_log2 = 4;
return true;
case 16:
*bytes_per_element = 16;
*texels_per_element_wide = 4;
*texels_per_element_tall = 4;
*block_width_log2 = 4;
*block_height_log2 = 4;
return true;
default: return false;
}
}
bool TileGetStandard4KBVolumeLayout(uint32_t format, uint32_t* bytes_per_element,
uint32_t* texels_per_element_wide,
uint32_t* texels_per_element_tall, uint32_t* block_width_log2,
uint32_t* block_height_log2, uint32_t* block_depth_log2) {
if (!Gen5Standard4KBLayout(format, bytes_per_element, texels_per_element_wide,
texels_per_element_tall, block_width_log2, block_height_log2)) {
return false;
}
switch (*bytes_per_element) {
case 1:
*block_width_log2 = 4;
*block_height_log2 = 4;
*block_depth_log2 = 4;
return true;
case 2:
*block_width_log2 = 3;
*block_height_log2 = 4;
*block_depth_log2 = 4;
return true;
case 4:
*block_width_log2 = 3;
*block_height_log2 = 4;
*block_depth_log2 = 3;
return true;
case 8:
*block_width_log2 = 3;
*block_height_log2 = 3;
*block_depth_log2 = 3;
return true;
case 16:
*block_width_log2 = 2;
*block_height_log2 = 3;
*block_depth_log2 = 3;
return true;
default: break;
}
return false;
}
static bool Gen5Standard256BLayout(uint32_t format, uint32_t* bytes_per_element,
uint32_t* texels_per_element_wide,
uint32_t* texels_per_element_tall, uint32_t* block_width_log2,
uint32_t* block_height_log2) {
const auto bytes = Prospero::NumBytesPerElement(format);
switch (bytes) {
case 1:
*bytes_per_element = 1;
*texels_per_element_wide = 1;
*texels_per_element_tall = 1;
*block_width_log2 = 4;
*block_height_log2 = 4;
return true;
case 2:
*bytes_per_element = 2;
*texels_per_element_wide = 1;
*texels_per_element_tall = 1;
*block_width_log2 = 4;
*block_height_log2 = 3;
return true;
case 4:
*bytes_per_element = 4;
*texels_per_element_wide = 1;
*texels_per_element_tall = 1;
*block_width_log2 = 3;
*block_height_log2 = 3;
return true;
case 8:
*bytes_per_element = 8;
*texels_per_element_wide = 1;
*texels_per_element_tall = 1;
*block_width_log2 = 3;
*block_height_log2 = 2;
return true;
case 16:
*bytes_per_element = 16;
*texels_per_element_wide = 1;
*texels_per_element_tall = 1;
*block_width_log2 = 2;
*block_height_log2 = 2;
return true;
default: break;
}
switch (Prospero::BlockCompressedBytesPerBlock(format)) {
case 8:
*bytes_per_element = 8;
*texels_per_element_wide = 4;
*texels_per_element_tall = 4;
*block_width_log2 = 3;
*block_height_log2 = 2;
return true;
case 16:
*bytes_per_element = 16;
*texels_per_element_wide = 4;
*texels_per_element_tall = 4;
*block_width_log2 = 2;
*block_height_log2 = 2;
return true;
default: return false;
}
}
static bool Gen5Standard64KBLayout(uint32_t format, uint32_t* bytes_per_element,
uint32_t* texels_per_element_wide,
uint32_t* texels_per_element_tall, uint32_t* block_width_log2,
uint32_t* block_height_log2) {
switch (Prospero::NumBytesPerElement(format)) {
case 1:
*bytes_per_element = 1;
*texels_per_element_wide = 1;
*texels_per_element_tall = 1;
*block_width_log2 = 8;
*block_height_log2 = 8;
return true;
case 2:
*bytes_per_element = 2;
*texels_per_element_wide = 1;
*texels_per_element_tall = 1;
*block_width_log2 = 8;
*block_height_log2 = 7;
return true;
case 4:
*bytes_per_element = 4;
*texels_per_element_wide = 1;
*texels_per_element_tall = 1;
*block_width_log2 = 7;
*block_height_log2 = 7;
return true;
case 8:
*bytes_per_element = 8;
*texels_per_element_wide = 1;
*texels_per_element_tall = 1;
*block_width_log2 = 7;
*block_height_log2 = 6;
return true;
case 16:
*bytes_per_element = 16;
*texels_per_element_wide = 1;
*texels_per_element_tall = 1;
*block_width_log2 = 6;
*block_height_log2 = 6;
return true;
default: break;
}
switch (Prospero::BlockCompressedBytesPerBlock(format)) {
case 8:
*bytes_per_element = 8;
*texels_per_element_wide = 4;
*texels_per_element_tall = 4;
*block_width_log2 = 7;
*block_height_log2 = 6;
return true;
case 16:
*bytes_per_element = 16;
*texels_per_element_wide = 4;
*texels_per_element_tall = 4;
*block_width_log2 = 6;
*block_height_log2 = 6;
return true;
default: return false;
}
}
static bool Gen5Thin64KBBlockSizeFromElementBytes(uint32_t bytes_per_element,
uint32_t* block_width,
uint32_t* block_height) {
// AGC thin 64 KiB block table, shared by depth and render-target tiles.
switch (bytes_per_element) {
case 1:
*block_width = 256;
*block_height = 256;
return true;
case 2:
*block_width = 256;
*block_height = 128;
return true;
case 4:
*block_width = 128;
*block_height = 128;
return true;
case 8:
*block_width = 128;
*block_height = 64;
return true;
case 16:
*block_width = 64;
*block_height = 64;
return true;
default: break;
}
return false;
}
struct Gen5MipTailLocation {
uint32_t x;
uint32_t y;
};
static constexpr Gen5MipTailLocation GEN5_MIP_TAIL_LOCATIONS_THIN_4KB[5][8] = {
{{32, 0}, {16, 32}, {0, 48}, {0, 32}, {16, 16}, {16, 0}, {0, 16}, {0, 0}},
{{32, 0}, {16, 16}, {0, 24}, {0, 16}, {16, 8}, {16, 0}, {0, 8}, {0, 0}},
{{16, 0}, {8, 16}, {0, 24}, {0, 16}, {8, 8}, {8, 0}, {0, 8}, {0, 0}},
{{16, 0}, {8, 8}, {0, 12}, {0, 8}, {8, 4}, {8, 0}, {0, 4}, {0, 0}},
{{8, 0}, {4, 8}, {0, 12}, {0, 8}, {4, 4}, {4, 0}, {0, 4}, {0, 0}},
};
static constexpr Gen5MipTailLocation GEN5_MIP_TAIL_LOCATIONS_THIN_64KB[5][12] = {
{{128, 0},
{0, 128},
{64, 0},
{0, 64},
{32, 0},
{16, 32},
{0, 48},
{0, 32},
{16, 16},
{16, 0},
{0, 16},
{0, 0}},
{{128, 0},
{0, 64},
{64, 0},
{0, 32},
{32, 0},
{16, 16},
{0, 24},
{0, 16},
{16, 8},
{16, 0},
{0, 8},
{0, 0}},
{{64, 0},
{0, 64},
{32, 0},
{0, 32},
{16, 0},
{8, 16},
{0, 24},
{0, 16},
{8, 8},
{8, 0},
{0, 8},
{0, 0}},
{{64, 0},
{0, 32},
{32, 0},
{0, 16},
{16, 0},
{8, 8},
{0, 12},
{0, 8},
{8, 4},
{8, 0},
{0, 4},
{0, 0}},
{{32, 0},
{0, 32},
{16, 0},
{0, 16},
{8, 0},
{4, 8},
{0, 12},
{0, 8},
{4, 4},
{4, 0},
{0, 4},
{0, 0}},
};
static uint32_t Gen5Standard4KBOffsetInBlock(uint32_t x, uint32_t y, uint32_t bytes_per_element) {
uint32_t offset = 0;
switch (bytes_per_element) {
case 1:
offset ^= (y << 4u) & 0x1f0u;
offset ^= (y << 5u) & 0x400u;
offset ^= x & 0x00fu;
offset ^= (x << 5u) & 0x200u;
offset ^= (x << 6u) & 0x800u;
return offset;
case 2:
offset ^= (y << 4u) & 0x070u;
offset ^= (y << 5u) & 0x100u;
offset ^= (y << 6u) & 0x400u;
offset ^= (x << 1u) & 0x00eu;
offset ^= (x << 4u) & 0x080u;
offset ^= (x << 5u) & 0x200u;
offset ^= (x << 6u) & 0x800u;
return offset;
case 4:
offset ^= (y << 4u) & 0x070u;
offset ^= (y << 5u) & 0x100u;
offset ^= (y << 6u) & 0x400u;
offset ^= (x << 2u) & 0x00cu;
offset ^= (x << 5u) & 0x080u;
offset ^= (x << 6u) & 0x200u;
offset ^= (x << 7u) & 0x800u;
return offset;
case 8:
offset ^= (y << 4u) & 0x030u;
offset ^= (y << 6u) & 0x100u;
offset ^= (y << 7u) & 0x400u;
offset ^= (x << 3u) & 0x008u;
offset ^= (x << 5u) & 0x0c0u;
offset ^= (x << 6u) & 0x200u;
offset ^= (x << 7u) & 0x800u;
return offset;
case 16:
offset ^= (y << 4u) & 0x030u;
offset ^= (y << 6u) & 0x100u;
offset ^= (y << 7u) & 0x400u;
offset ^= (x << 6u) & 0x0c0u;
offset ^= (x << 7u) & 0x200u;
offset ^= (x << 8u) & 0x800u;
return offset;
default: EXIT("unsupported Standard4KB element size: %u\n", bytes_per_element);
}
return 0;
}
static uint32_t Gen5Standard4KBVolumeOffsetInBlock(uint32_t x, uint32_t y, uint32_t z,
uint32_t bytes_per_element) {
uint32_t offset = 0;
switch (bytes_per_element) {
case 1:
offset ^= x & 0x3u;
offset ^= (x << 4u) & 0x40u;
offset ^= (x << 6u) & 0x200u;
offset ^= (y << 3u) & 0x8u;
offset ^= (y << 4u) & 0x20u;
offset ^= (y << 6u) & 0x100u;
offset ^= (y << 8u) & 0x800u;
offset ^= (z << 2u) & 0x4u;
offset ^= (z << 3u) & 0x10u;
offset ^= (z << 5u) & 0x80u;
offset ^= (z << 7u) & 0x400u;
return offset;
case 2:
offset ^= (x << 1u) & 0x2u;
offset ^= (x << 5u) & 0x40u;
offset ^= (x << 7u) & 0x200u;
offset ^= (y << 3u) & 0x8u;
offset ^= (y << 4u) & 0x20u;
offset ^= (y << 6u) & 0x100u;
offset ^= (y << 8u) & 0x800u;
offset ^= (z << 2u) & 0x4u;
offset ^= (z << 3u) & 0x10u;
offset ^= (z << 5u) & 0x80u;
offset ^= (z << 7u) & 0x400u;
return offset;
case 4:
offset ^= (x << 2u) & 0x4u;
offset ^= (x << 5u) & 0x40u;
offset ^= (x << 7u) & 0x200u;
offset ^= (y << 3u) & 0x8u;
offset ^= (y << 4u) & 0x20u;
offset ^= (y << 6u) & 0x100u;
offset ^= (y << 8u) & 0x800u;
offset ^= (z << 4u) & 0x10u;
offset ^= (z << 6u) & 0x80u;
offset ^= (z << 8u) & 0x400u;
return offset;
case 8:
offset ^= (x << 3u) & 0x8u;
offset ^= (x << 5u) & 0x40u;
offset ^= (x << 7u) & 0x200u;
offset ^= (y << 5u) & 0x20u;
offset ^= (y << 7u) & 0x100u;
offset ^= (y << 9u) & 0x800u;
offset ^= (z << 4u) & 0x10u;
offset ^= (z << 6u) & 0x80u;
offset ^= (z << 8u) & 0x400u;
return offset;
case 16:
offset ^= (x << 6u) & 0x40u;
offset ^= (x << 8u) & 0x200u;
offset ^= (y << 5u) & 0x20u;
offset ^= (y << 7u) & 0x100u;
offset ^= (y << 9u) & 0x800u;
offset ^= (z << 4u) & 0x10u;
offset ^= (z << 6u) & 0x80u;
offset ^= (z << 8u) & 0x400u;
return offset;
default: EXIT("unsupported Standard4KB volume element size: %u\n", bytes_per_element);
}
return 0;
}
bool TileIsStandard4KBTextureSupported(uint32_t format) {
uint32_t bytes_per_element = 0;
uint32_t texels_per_element_wide = 0;
uint32_t texels_per_element_tall = 0;
uint32_t block_width_log2 = 0;
uint32_t block_height_log2 = 0;
return Gen5Standard4KBLayout(format, &bytes_per_element, &texels_per_element_wide,
&texels_per_element_tall, &block_width_log2, &block_height_log2);
}
bool TileIsStandard256BTextureSupported(uint32_t format) {
uint32_t bytes_per_element = 0;
uint32_t texels_per_element_wide = 0;
uint32_t texels_per_element_tall = 0;
uint32_t block_width_log2 = 0;
uint32_t block_height_log2 = 0;
return Gen5Standard256BLayout(format, &bytes_per_element, &texels_per_element_wide,
&texels_per_element_tall, &block_width_log2, &block_height_log2);
}
bool TileIsStandard64KBTextureSupported(uint32_t format) {
uint32_t bytes_per_element = 0;
uint32_t texels_per_element_wide = 0;
uint32_t texels_per_element_tall = 0;
uint32_t block_width_log2 = 0;
uint32_t block_height_log2 = 0;
return Gen5Standard64KBLayout(format, &bytes_per_element, &texels_per_element_wide,
&texels_per_element_tall, &block_width_log2, &block_height_log2);
}
struct Uint128 {
uint64_t n[2];
};
struct Uint256 {
Uint128 n[2];
};
template <typename T>
static void DetileStandard4KBTyped(T* dst, const T* src, uint32_t row_elements,
uint32_t width_elements, uint32_t height_elements,
uint32_t padded_width, uint32_t block_width_log2,
uint32_t block_height_log2, uint64_t dst_size, uint64_t src_size,
uint32_t src_x, uint32_t src_y) {
constexpr auto bytes_per_element = static_cast<uint32_t>(sizeof(T));
std::array<uint32_t, 64> x_elements {};
std::array<uint32_t, 64> y_elements {};
const uint32_t block_width = 1u << block_width_log2;
const uint32_t block_height = 1u << block_height_log2;
const uint32_t elements_per_block = 4096u / bytes_per_element;
const uint32_t blocks_per_row = padded_width >> block_width_log2;
for (uint32_t x = 0; x < block_width; x++) {
x_elements[x] = Gen5Standard4KBOffsetInBlock(x, 0, bytes_per_element) / bytes_per_element;
}
for (uint32_t y = 0; y < block_height; y++) {
y_elements[y] = Gen5Standard4KBOffsetInBlock(0, y, bytes_per_element) / bytes_per_element;
}
const uint64_t dst_count = dst_size / bytes_per_element;
const uint64_t src_count = src_size / bytes_per_element;
for (uint32_t block_y = 0; block_y < height_elements; block_y += block_height) {
const uint32_t copy_height = std::min(block_height, height_elements - block_y);
for (uint32_t block_x = 0; block_x < width_elements; block_x += block_width) {
const uint32_t copy_width = std::min(block_width, width_elements - block_x);
const uint64_t block_index =
(static_cast<uint64_t>(block_y >> block_height_log2) * blocks_per_row) +
(block_x >> block_width_log2);
const uint64_t block_base = block_index * elements_per_block;
for (uint32_t local_y = 0; local_y < copy_height; local_y++) {
const uint64_t dst_row =
(static_cast<uint64_t>(block_y + local_y) * row_elements) + block_x;
const uint64_t src_row = block_base + y_elements[src_y + local_y];
for (uint32_t local_x = 0; local_x < copy_width; local_x++) {
const uint64_t dst_index = dst_row + local_x;
const uint64_t src_index = src_row + x_elements[src_x + local_x];
if (src_index < src_count && dst_index < dst_count) {
dst[dst_index] = src[src_index];
}
}
}
}
}
}
template <typename T>
static void DetileStandard4KBVolumeTyped(T* dst, const T* src, uint32_t row_elements,
uint32_t width_elements, uint32_t height_elements,
uint32_t depth_elements, uint32_t padded_width,
uint32_t padded_height, uint32_t block_width_log2,
uint32_t block_height_log2, uint32_t block_depth_log2,
uint64_t dst_slice_stride, uint64_t dst_size,
uint64_t src_size) {
constexpr auto bytes_per_element = static_cast<uint32_t>(sizeof(T));
const uint32_t block_width = 1u << block_width_log2;
const uint32_t block_height = 1u << block_height_log2;
const uint32_t block_depth = 1u << block_depth_log2;
const uint32_t elements_per_block = 4096u / bytes_per_element;
const uint32_t blocks_per_row = padded_width >> block_width_log2;
const uint32_t blocks_per_column = padded_height >> block_height_log2;
const uint64_t blocks_per_slice =
static_cast<uint64_t>(blocks_per_row) * static_cast<uint64_t>(blocks_per_column);
const uint64_t dst_count = dst_size / bytes_per_element;
const uint64_t src_count = src_size / bytes_per_element;
const uint64_t dst_slice_elements = dst_slice_stride / bytes_per_element;
for (uint32_t block_z = 0; block_z < depth_elements; block_z += block_depth) {
const uint32_t copy_depth = std::min(block_depth, depth_elements - block_z);
for (uint32_t block_y = 0; block_y < height_elements; block_y += block_height) {
const uint32_t copy_height = std::min(block_height, height_elements - block_y);
for (uint32_t block_x = 0; block_x < width_elements; block_x += block_width) {
const uint32_t copy_width = std::min(block_width, width_elements - block_x);
const uint64_t block_index =
((static_cast<uint64_t>(block_z >> block_depth_log2) * blocks_per_slice) +
(static_cast<uint64_t>(block_y >> block_height_log2) * blocks_per_row) +
(block_x >> block_width_log2));
const uint64_t block_base = block_index * elements_per_block;
for (uint32_t local_z = 0; local_z < copy_depth; local_z++) {
for (uint32_t local_y = 0; local_y < copy_height; local_y++) {
const uint64_t dst_row =
(static_cast<uint64_t>(block_z + local_z) * dst_slice_elements) +
(static_cast<uint64_t>(block_y + local_y) * row_elements) + block_x;
for (uint32_t local_x = 0; local_x < copy_width; local_x++) {
const uint64_t dst_index = dst_row + local_x;
const uint64_t src_index =
block_base + (Gen5Standard4KBVolumeOffsetInBlock(
local_x, local_y, local_z, bytes_per_element) /
bytes_per_element);
if (src_index < src_count && dst_index < dst_count) {
dst[dst_index] = src[src_index];
}
}
}
}
}
}
}
}
static uint32_t Gen5Standard256BOffsetInBlock(uint32_t x, uint32_t y, uint32_t bytes_per_element) {
return Gen5Standard4KBOffsetInBlock(x, y, bytes_per_element) & 0xffu;
}
template <typename T>
static void
DetileStandard256BTyped(T* dst, const T* src, uint32_t row_elements, uint32_t width_elements,
uint32_t height_elements, uint32_t padded_width, uint32_t block_width_log2,
uint32_t block_height_log2, uint64_t dst_size, uint64_t src_size) {
constexpr auto bytes_per_element = static_cast<uint32_t>(sizeof(T));
std::array<uint32_t, 16> x_elements {};
std::array<uint32_t, 16> y_elements {};
const uint32_t block_width = 1u << block_width_log2;
const uint32_t block_height = 1u << block_height_log2;
const uint32_t elements_per_block = 256u / bytes_per_element;
const uint32_t blocks_per_row = padded_width >> block_width_log2;
for (uint32_t x = 0; x < block_width; x++) {
x_elements[x] = Gen5Standard256BOffsetInBlock(x, 0, bytes_per_element) / bytes_per_element;
}
for (uint32_t y = 0; y < block_height; y++) {
y_elements[y] = Gen5Standard256BOffsetInBlock(0, y, bytes_per_element) / bytes_per_element;
}
const uint64_t dst_count = dst_size / bytes_per_element;
const uint64_t src_count = src_size / bytes_per_element;
for (uint32_t block_y = 0; block_y < height_elements; block_y += block_height) {
const uint32_t copy_height = std::min(block_height, height_elements - block_y);
for (uint32_t block_x = 0; block_x < width_elements; block_x += block_width) {
const uint32_t copy_width = std::min(block_width, width_elements - block_x);
const uint64_t block_index =
(static_cast<uint64_t>(block_y >> block_height_log2) * blocks_per_row) +
(block_x >> block_width_log2);
const uint64_t block_base = block_index * elements_per_block;
for (uint32_t local_y = 0; local_y < copy_height; local_y++) {
const uint64_t dst_row =
(static_cast<uint64_t>(block_y + local_y) * row_elements) + block_x;
const uint64_t src_row = block_base + y_elements[local_y];
for (uint32_t local_x = 0; local_x < copy_width; local_x++) {
const uint64_t dst_index = dst_row + local_x;
const uint64_t src_index = src_row + x_elements[local_x];
if (src_index < src_count && dst_index < dst_count) {
dst[dst_index] = src[src_index];
}
}
}
}
}
}
class Tiler {
public:
Tiler() { EXIT_NOT_IMPLEMENTED(!Common::Thread::IsMainThread()); }
~Tiler() { KYTY_NOT_IMPLEMENTED; }
KYTY_CLASS_NO_COPY(Tiler);
Common::Mutex m_mutex;
AsyncJob m_job1;
};
class Tiler32 {
public:
uint32_t m_macro_tile_height = 0;
uint32_t m_bank_height = 0;
uint32_t m_num_banks = 0;
uint32_t m_num_pipes = 0;
uint32_t m_padded_width = 0;
uint32_t m_padded_height = 0;
uint32_t m_pipe_bits = 0;
uint32_t m_bank_bits = 0;
void Init(uint32_t width, uint32_t height) {
m_macro_tile_height = 128;
m_bank_height = 2;
m_num_banks = 8;
m_num_pipes = 16;
m_padded_width = AlignUp(width, 128);
m_padded_height = AlignUp(height, m_macro_tile_height);
m_pipe_bits = 4;
m_bank_bits = 3;
}
static uint32_t GetElementIndex(uint32_t x, uint32_t y) {
uint32_t elem = 0;
elem |= ((x >> 0u) & 0x1u) << 0u;
elem |= ((x >> 1u) & 0x1u) << 1u;
elem |= ((y >> 0u) & 0x1u) << 2u;
elem |= ((x >> 2u) & 0x1u) << 3u;
elem |= ((y >> 1u) & 0x1u) << 4u;
elem |= ((y >> 2u) & 0x1u) << 5u;
return elem;
}
static uint32_t GetPipeIndex(uint32_t x, uint32_t y) {
uint32_t pipe = 0;
pipe |= (((x >> 3u) ^ (y >> 3u) ^ (x >> 4u)) & 0x1u) << 0u;
pipe |= (((x >> 4u) ^ (y >> 4u)) & 0x1u) << 1u;
pipe |= (((x >> 5u) ^ (y >> 5u)) & 0x1u) << 2u;
pipe |= (((x >> 6u) ^ (y >> 5u)) & 0x1u) << 3u;
return pipe;
}
static uint32_t GetBankIndex(uint32_t x, uint32_t y, uint32_t bank_width, uint32_t bank_height,
uint32_t num_banks, uint32_t num_pipes) {
const uint32_t x_shift_offset = IntLog2(bank_width * num_pipes);
const uint32_t y_shift_offset = IntLog2(bank_height);
const uint32_t xs = x >> x_shift_offset;
const uint32_t ys = y >> y_shift_offset;
uint32_t bank = 0;
switch (num_banks) {
case 8:
bank |= (((xs >> 3u) ^ (ys >> 5u)) & 0x1u) << 0u;
bank |= (((xs >> 4u) ^ (ys >> 4u) ^ (ys >> 5u)) & 0x1u) << 1u;
bank |= (((xs >> 5u) ^ (ys >> 3u)) & 0x1u) << 2u;
break;
case 16:
bank |= (((xs >> 3u) ^ (ys >> 6u)) & 0x1u) << 0u;
bank |= (((xs >> 4u) ^ (ys >> 5u) ^ (ys >> 6u)) & 0x1u) << 1u;
bank |= (((xs >> 5u) ^ (ys >> 4u)) & 0x1u) << 2u;
bank |= (((xs >> 6u) ^ (ys >> 3u)) & 0x1u) << 3u;
break;
default:;
}
return bank;
}
[[nodiscard]] uint64_t GetTiledOffset(uint32_t x, uint32_t y) const {
return GetTiledOffset(x, y, 32);
}
[[nodiscard]] uint64_t GetTiledOffset(uint32_t x, uint32_t y, uint32_t bits_per_element) const {
uint64_t element_index = GetElementIndex(x, y);
uint32_t xh = x;
uint32_t yh = y;
uint64_t pipe = GetPipeIndex(xh, yh);
uint64_t bank = GetBankIndex(xh, yh, 1, m_bank_height, m_num_banks, m_num_pipes);
uint32_t tile_bytes = (8 * 8 * bits_per_element + 7) / 8;
uint64_t element_offset = (element_index * bits_per_element);
uint64_t tile_split_slice = 0;
if (tile_bytes > 512) {
tile_split_slice = element_offset / (static_cast<uint64_t>(512) * 8);
element_offset %= (static_cast<uint64_t>(512) * 8);
tile_bytes = 512;
}
uint64_t macro_tile_bytes =
(128 / 8) * (m_macro_tile_height / 8) * tile_bytes / (m_num_pipes * m_num_banks);
uint64_t macro_tiles_per_row = m_padded_width / 128;
uint64_t macro_tile_row_index = y / m_macro_tile_height;
uint64_t macro_tile_column_index = x / 128;
uint64_t macro_tile_index =
(macro_tile_row_index * macro_tiles_per_row) + macro_tile_column_index;
uint64_t macro_tile_offset = macro_tile_index * macro_tile_bytes;
uint64_t macro_tiles_per_slice =
macro_tiles_per_row * (m_padded_height / m_macro_tile_height);
uint64_t slice_bytes = macro_tiles_per_slice * macro_tile_bytes;
uint64_t slice_offset = tile_split_slice * slice_bytes;
uint64_t tile_row_index = (y / 8) % m_bank_height;
uint64_t tile_index = tile_row_index;
uint64_t tile_offset = tile_index * tile_bytes;
uint64_t tile_split_slice_rotation = ((m_num_banks / 2) + 1) * tile_split_slice;
bank ^= tile_split_slice_rotation;
bank &= (m_num_banks - 1);
uint64_t total_offset =
(slice_offset + macro_tile_offset + tile_offset) * 8 + element_offset;
uint64_t bit_offset = total_offset & 0x7u;
total_offset /= 8;
uint64_t pipe_interleave_offset = total_offset & 0xffu;
uint64_t offset = total_offset >> 8u;
uint64_t byte_offset = pipe_interleave_offset | (pipe << (8u)) |
(bank << (8u + m_pipe_bits)) |
(offset << (8u + m_pipe_bits + m_bank_bits));
return ((byte_offset << 3u) | bit_offset) / 8;
}
};
struct FastTiler32XInfo {
uint64_t macro_offset = 0;
uint16_t element_bytes = 0;
uint8_t pipe = 0;
uint8_t bank = 0;
};
struct FastTiler32YInfo {
uint64_t macro_offset = 0;
uint16_t tile_offset = 0;
uint16_t element_bytes = 0;
uint8_t pipe = 0;
uint8_t bank = 0;
};
class FastTiler32 {
public:
void Init(const Tiler32& t, uint32_t width, uint32_t height) {
constexpr uint32_t bytes_per_element = 4;
const uint32_t tile_bytes = 8u * 8u * bytes_per_element;
const uint64_t macro_tile_bytes = (128u / 8u) * (t.m_macro_tile_height / 8u) * tile_bytes /
(t.m_num_pipes * t.m_num_banks);
const uint64_t macro_tiles_per_row = t.m_padded_width / 128u;
m_x.resize(width);
m_y.resize(height);
m_pipe_bits = t.m_pipe_bits;
m_bank_bits = t.m_bank_bits;
for (uint32_t x = 0; x < width; x++) {
auto& info = m_x[x];
info.macro_offset = (x / 128u) * macro_tile_bytes;
info.element_bytes =
static_cast<uint16_t>(Tiler32::GetElementIndex(x, 0) * bytes_per_element);
info.pipe = static_cast<uint8_t>(Tiler32::GetPipeIndex(x, 0));
info.bank = static_cast<uint8_t>(
Tiler32::GetBankIndex(x, 0, 1, t.m_bank_height, t.m_num_banks, t.m_num_pipes));
}
for (uint32_t y = 0; y < height; y++) {
auto& info = m_y[y];
info.macro_offset = (static_cast<uint64_t>(y) / t.m_macro_tile_height) *
macro_tiles_per_row * macro_tile_bytes;
info.tile_offset = static_cast<uint16_t>(((y / 8u) % t.m_bank_height) * tile_bytes);
info.element_bytes =
static_cast<uint16_t>(Tiler32::GetElementIndex(0, y) * bytes_per_element);
info.pipe = static_cast<uint8_t>(Tiler32::GetPipeIndex(0, y));
info.bank = static_cast<uint8_t>(
Tiler32::GetBankIndex(0, y, 1, t.m_bank_height, t.m_num_banks, t.m_num_pipes));
}
}
[[nodiscard]] uint64_t GetTiledOffset(uint32_t x, uint32_t y) const {
const auto& x_info = m_x[x];
const auto& y_info = m_y[y];
const uint64_t base = y_info.macro_offset + x_info.macro_offset + y_info.tile_offset +
y_info.element_bytes + x_info.element_bytes;
const uint64_t pipe = x_info.pipe ^ y_info.pipe;
const uint64_t bank = x_info.bank ^ y_info.bank;
return (base & 0xffu) | (pipe << 8u) | (bank << (8u + m_pipe_bits)) |
((base >> 8u) << (8u + m_pipe_bits + m_bank_bits));
}
private:
std::vector<FastTiler32XInfo> m_x;
std::vector<FastTiler32YInfo> m_y;
uint32_t m_pipe_bits = 0;
uint32_t m_bank_bits = 0;
};
static Tiler* g_tiler = nullptr;
void TileInit() {
EXIT_IF(g_tiler != nullptr);
g_tiler = new Tiler;
}
// NOLINTNEXTLINE(readability-non-const-parameter)
static void Detile32(const Tiler32& t, uint32_t width, uint32_t height, uint32_t dst_pitch,
uint8_t* dst, const uint8_t* src) {
EXIT_IF(g_tiler == nullptr);
Common::LockGuard lock(g_tiler->m_mutex);
FastTiler32 fast_tiler;
fast_tiler.Init(t, width, height);
struct DetileParams {
const FastTiler32* t;
uint32_t start_y;
uint32_t width;
uint32_t height;
uint32_t dst_pitch;
uint8_t* dst;
const uint8_t* src;
};
auto func = [](void* args) {
auto* p = static_cast<DetileParams*>(args);
auto* dst = p->dst;
const auto* src = p->src;
const FastTiler32* t = p->t;
uint32_t start_y = p->start_y;
uint32_t width = p->width;
uint32_t height = p->height;
uint64_t dst_pitch = p->dst_pitch;
for (uint32_t y = start_y; y < height; y++) {
uint32_t x = 0;
uint64_t linear_offset = y * dst_pitch * 4;
for (; x + 1 < width; x += 2) {
auto tiled_offset = t->GetTiledOffset(x, y);
*reinterpret_cast<uint64_t*>(dst + linear_offset) =
*reinterpret_cast<const uint64_t*>(src + tiled_offset);
linear_offset += 8;
}
if (x < width) {
auto tiled_offset = t->GetTiledOffset(x, y);
*reinterpret_cast<uint32_t*>(dst + linear_offset) =
*reinterpret_cast<const uint32_t*>(src + tiled_offset);
}
}
};
const uint32_t split = height / 2;
DetileParams p1 {&fast_tiler, 0, width, split, dst_pitch, dst, src};
DetileParams p2 {&fast_tiler, split, width, height, dst_pitch, dst, src};
g_tiler->m_job1.Execute([func, &p2] { func(&p2); });
func(&p1);
g_tiler->m_job1.Wait();
}
static constexpr uint32_t GetStandard64KB32XPart(uint32_t x) {
// FIXME: Temporary PS5 AGC TileMode::kStandard64KB 32bpp detiler.
// AGC Standard64KB is block-linear: 32bpp surfaces use 128x128-element
// 64KB blocks, with fixed x/y bit interleaving inside each block.
uint32_t element_offset = 0;
element_offset ^= (x << 2u) & 0x0cu;
element_offset ^= (x << 5u) & 0x80u;
element_offset ^= (x << 6u) & 0x200u;
element_offset ^= (x << 7u) & 0x800u;
element_offset ^= (x << 8u) & 0x2000u;
// X6 selects the upper 32KB of each 128x128x32bpp block.
element_offset ^= (x << 9u) & 0x8000u;
return element_offset;
}
static constexpr uint32_t GetStandard64KB32YPart(uint32_t y) {
uint32_t element_offset = 0;
element_offset ^= (y << 4u) & 0x70u;
element_offset ^= (y << 5u) & 0x100u;
element_offset ^= (y << 6u) & 0x400u;
element_offset ^= (y << 7u) & 0x1000u;
element_offset ^= (y << 8u) & 0x4000u;
return element_offset;
}
struct Standard64KB32Tables {
std::array<uint32_t, 128> x_words {};
std::array<uint32_t, 128> y_words {};
constexpr Standard64KB32Tables() {
for (uint32_t i = 0; i < 128; i++) {
uint32_t x_part = 0;
x_part ^= (i << 2u) & 0x0cu;
x_part ^= (i << 5u) & 0x80u;
x_part ^= (i << 6u) & 0x200u;
x_part ^= (i << 7u) & 0x800u;
x_part ^= (i << 8u) & 0x2000u;
x_part ^= (i << 9u) & 0x8000u;
uint32_t y_part = 0;
y_part ^= (i << 4u) & 0x70u;
y_part ^= (i << 5u) & 0x100u;
y_part ^= (i << 6u) & 0x400u;
y_part ^= (i << 7u) & 0x1000u;
y_part ^= (i << 8u) & 0x4000u;
x_words[i] = x_part >> 2u;
y_words[i] = y_part >> 2u;
}
}
};
static_assert(GetStandard64KB32XPart(127) < (64u * 1024u));
static_assert(GetStandard64KB32YPart(127) < (64u * 1024u));
static const Standard64KB32Tables& GetStandard64KB32Tables() {
static constexpr Standard64KB32Tables tables;
return tables;
}
struct Standard64KB16Tables {
std::array<uint32_t, 256> x_words {};
std::array<uint32_t, 128> y_words {};
constexpr Standard64KB16Tables() {
for (uint32_t i = 0; i < 256; i++) {
uint32_t x_part = 0;
x_part ^= (i << 1u) & 0x0006u;
x_part ^= (i << 4u) & 0x0040u;
x_part ^= (i << 5u) & 0x0100u;
x_part ^= (i << 6u) & 0x0400u;
x_part ^= (i << 7u) & 0x1000u;
x_part ^= (i << 8u) & 0x4000u;
x_part ^= (i << 8u) & 0x8000u;
x_words[i] = x_part >> 1u;
}
for (uint32_t i = 0; i < 128; i++) {
uint32_t y_part = 0;
y_part ^= (i << 3u) & 0x0038u;
y_part ^= (i << 4u) & 0x0080u;
y_part ^= (i << 5u) & 0x0200u;
y_part ^= (i << 6u) & 0x0800u;
y_part ^= (i << 7u) & 0x2000u;
y_words[i] = y_part >> 1u;
}
}
};
template <typename T>
static uint32_t Gen5RenderTargetOffsetInBlock(uint32_t x, uint32_t y) {
uint32_t offset = 0;
if constexpr (sizeof(T) == 1) {
offset ^= (y << 2u) & 0x0008u;
offset ^= (y << 4u) & 0x0010u;
offset ^= (y << 3u) & 0x00a0u;
offset ^= (y << 5u) & 0x0f00u;
offset ^= (y << 6u) & 0x1000u;
offset ^= (y << 7u) & 0x4000u;
offset ^= x & 0x0007u;
offset ^= (x << 3u) & 0x0040u;
offset ^= (x << 5u) & 0x0300u;
offset ^= (x << 4u) & 0x0400u;
offset ^= (x << 6u) & 0x0800u;
offset ^= (x << 7u) & 0x2000u;
offset ^= (x << 8u) & 0x8000u;
} else if constexpr (sizeof(T) == 2) {
offset ^= (y << 4u) & 0x0070u;
offset ^= (y << 5u) & 0x0f00u;
offset ^= (y << 8u) & 0x5000u;
offset ^= (x << 1u) & 0x000eu;
offset ^= (x << 4u) & 0x0480u;
offset ^= (x << 5u) & 0x0300u;
offset ^= (x << 6u) & 0x0800u;
offset ^= (x << 7u) & 0x2000u;
offset ^= (x << 8u) & 0x8000u;
} else if constexpr (sizeof(T) == 4) {
offset ^= (y << 3u) & 0x0008u;
offset ^= (y << 4u) & 0x0020u;
offset ^= (y << 5u) & 0x0f80u;
offset ^= (y << 9u) & 0x1000u;
offset ^= (y << 8u) & 0x4000u;
offset ^= (x << 2u) & 0x0004u;
offset ^= (x << 3u) & 0x0010u;
offset ^= (x << 4u) & 0x0440u;
offset ^= (x << 5u) & 0x0300u;
offset ^= (x << 6u) & 0x0800u;
offset ^= (x << 9u) & 0xa000u;
} else if constexpr (sizeof(T) == 8) {
offset ^= (y << 4u) & 0x0010u;
offset ^= (y << 6u) & 0x0080u;
offset ^= (y << 5u) & 0x0f00u;
offset ^= (y << 10u) & 0x5000u;
offset ^= (x << 3u) & 0x0008u;
offset ^= (x << 4u) & 0x0460u;
offset ^= (x << 5u) & 0x0300u;
offset ^= (x << 6u) & 0x0800u;
offset ^= (x << 10u) & 0x2000u;
offset ^= (x << 9u) & 0x8000u;
} else {
EXIT("unsupported render-target element size: %u\n", static_cast<uint32_t>(sizeof(T)));
}
return offset;
}
template <typename T, bool tiled_to_linear>
static void ConvertRenderTargetTyped(uint32_t width, uint32_t height, uint32_t pitch, T* dst,
const T* src, uint64_t size) {
EXIT_IF(g_tiler == nullptr);
uint32_t block_width = 0;
uint32_t block_height = 0;
EXIT_NOT_IMPLEMENTED(!Gen5Thin64KBBlockSizeFromElementBytes(
static_cast<uint32_t>(sizeof(T)), &block_width, &block_height));
const uint64_t blocks_per_row = (static_cast<uint64_t>(pitch) + block_width - 1u) / block_width;
const uint32_t block_columns = (width == 0 ? 0 : 1u + (width - 1u) / block_width);
const uint64_t elements_per_block = 65536u / sizeof(T);
const uint64_t count = size / sizeof(T);
std::array<uint32_t, 256> x_elements {};
std::array<uint32_t, 256> y_elements {};
for (uint32_t x = 0; x < block_width; x++) {
x_elements[x] = Gen5RenderTargetOffsetInBlock<T>(x, 0) / sizeof(T);
}
for (uint32_t y = 0; y < block_height; y++) {
y_elements[y] = Gen5RenderTargetOffsetInBlock<T>(0, y) / sizeof(T);
}
struct ConvertParams {
uint32_t start_block_row;
uint32_t end_block_row;
uint32_t width;
uint32_t height;
uint32_t pitch;
uint32_t block_width;
uint32_t block_height;
uint32_t block_columns;
uint64_t blocks_per_row;
uint64_t elements_per_block;
const uint32_t* x_elements;
const uint32_t* y_elements;
T* dst;
const T* src;
uint64_t count;
};
auto func = [](void* args) {
auto* p = static_cast<ConvertParams*>(args);
for (uint32_t block_row = p->start_block_row; block_row < p->end_block_row; block_row++) {
const uint32_t block_y = block_row * p->block_height;
const uint32_t copy_height = std::min(p->block_height, p->height - block_y);
const uint32_t block_y_element =
Gen5RenderTargetOffsetInBlock<T>(0, block_y) / sizeof(T);
for (uint32_t block_column = 0; block_column < p->block_columns; block_column++) {
const uint32_t block_x =
static_cast<uint32_t>(static_cast<uint64_t>(block_column) * p->block_width);
const uint32_t copy_width = std::min(p->block_width, p->width - block_x);
const uint64_t block_base =
(static_cast<uint64_t>(block_row) * p->blocks_per_row + block_column) *
p->elements_per_block;
for (uint32_t y = 0; y < copy_height; y++) {
const uint64_t linear_row =
static_cast<uint64_t>(block_y + y) * p->pitch + block_x;
for (uint32_t x = 0; x < copy_width; x++) {
const uint64_t linear_index = linear_row + x;
const uint64_t tiled_index =
block_base + (p->x_elements[x] ^ p->y_elements[y] ^ block_y_element);
if (linear_index < p->count && tiled_index < p->count) {
if constexpr (tiled_to_linear) {
p->dst[linear_index] = p->src[tiled_index];
} else {
p->dst[tiled_index] = p->src[linear_index];
}
}
}
}
}
}
};
const uint32_t block_rows = (height == 0 ? 0 : 1u + (height - 1u) / block_height);
const uint32_t split = block_rows / 2;
ConvertParams p1 {0,
split,
width,
height,
pitch,
block_width,
block_height,
block_columns,
blocks_per_row,
elements_per_block,
x_elements.data(),
y_elements.data(),
dst,
src,
count};
ConvertParams p2 {split,
block_rows,
width,
height,
pitch,
block_width,
block_height,
block_columns,
blocks_per_row,
elements_per_block,
x_elements.data(),
y_elements.data(),
dst,
src,
count};
Common::LockGuard lock(g_tiler->m_mutex);
g_tiler->m_job1.Execute([func, &p2] { func(&p2); });
func(&p1);
g_tiler->m_job1.Wait();
}
static const Standard64KB16Tables& GetStandard64KB16Tables() {
static constexpr Standard64KB16Tables tables;
return tables;
}
struct Standard64KB8Tables {
std::array<uint32_t, 256> x_words {};
std::array<uint32_t, 256> y_words {};
constexpr Standard64KB8Tables() {
for (uint32_t i = 0; i < 256; i++) {
uint32_t x_part = 0;
x_part ^= i & 0x000fu;
x_part ^= (i << 5u) & 0x0200u;
x_part ^= (i << 6u) & 0x0800u;
x_part ^= (i << 7u) & 0x2000u;
x_part ^= (i << 8u) & 0x8000u;
uint32_t y_part = 0;
y_part ^= (i << 4u) & 0x01f0u;
y_part ^= (i << 5u) & 0x0400u;
y_part ^= (i << 6u) & 0x1000u;
y_part ^= (i << 7u) & 0x4000u;
x_words[i] = x_part;
y_words[i] = y_part;
}
}
};
static const Standard64KB8Tables& GetStandard64KB8Tables() {
static constexpr Standard64KB8Tables tables;
return tables;
}
struct Standard64KB64Tables {
std::array<uint32_t, 128> x_words {};
std::array<uint32_t, 64> y_words {};
constexpr Standard64KB64Tables() {
for (uint32_t i = 0; i < 128; i++) {
uint32_t x_part = 0;
x_part ^= (i << 3u) & 0x0008u;
x_part ^= (i << 5u) & 0x00c0u;
x_part ^= (i << 6u) & 0x0200u;
x_part ^= (i << 7u) & 0x0800u;
x_part ^= (i << 8u) & 0x2000u;
x_part ^= (i << 9u) & 0x8000u;
x_words[i] = x_part >> 3u;
}
for (uint32_t i = 0; i < 64; i++) {
uint32_t y_part = 0;
y_part ^= (i << 4u) & 0x0030u;
y_part ^= (i << 6u) & 0x0100u;
y_part ^= (i << 7u) & 0x0400u;
y_part ^= (i << 8u) & 0x1000u;
y_part ^= (i << 9u) & 0x4000u;
y_words[i] = y_part >> 3u;
}
}
};
static const Standard64KB64Tables& GetStandard64KB64Tables() {
static constexpr Standard64KB64Tables tables;
return tables;
}
struct Standard64KB128Tables {
std::array<uint32_t, 64> x_words {};
std::array<uint32_t, 64> y_words {};
constexpr Standard64KB128Tables() {
for (uint32_t i = 0; i < 64; i++) {
uint32_t x_part = 0;
x_part ^= (i << 6u) & 0x00c0u;
x_part ^= (i << 7u) & 0x0200u;
x_part ^= (i << 8u) & 0x0800u;
x_part ^= (i << 9u) & 0x2000u;
x_part ^= (i << 10u) & 0x8000u;
uint32_t y_part = 0;
y_part ^= (i << 4u) & 0x0030u;
y_part ^= (i << 6u) & 0x0100u;
y_part ^= (i << 7u) & 0x0400u;
y_part ^= (i << 8u) & 0x1000u;
y_part ^= (i << 9u) & 0x4000u;
x_words[i] = x_part >> 4u;
y_words[i] = y_part >> 4u;
}
}
};
static const Standard64KB128Tables& GetStandard64KB128Tables() {
static constexpr Standard64KB128Tables tables;
return tables;
}
void TileConvertTiledToLinearStandard64KB32(void* dst, const void* src, uint32_t width,
uint32_t height, uint32_t pitch, uint64_t size,
uint64_t src_size, uint32_t src_x, uint32_t src_y) {
KYTY_PROFILER_FUNCTION();
EXIT_IF(dst == nullptr || src == nullptr);
EXIT_NOT_IMPLEMENTED(pitch == 0);
const bool tail_block = (src_size != 0 && (src_x != 0 || src_y != 0 || size < src_size));
EXIT_NOT_IMPLEMENTED(width > pitch);
EXIT_NOT_IMPLEMENTED(tail_block && (src_x + width > 128u || src_y + height > 128u));
auto* dst32 = static_cast<uint32_t*>(dst);
const auto* src32 = static_cast<const uint32_t*>(src);
const auto& tables = GetStandard64KB32Tables();
const auto size_words = size >> 2u;
const auto src_size_words = (src_size != 0 ? src_size : size) >> 2u;
const auto blocks_per_row =
(tail_block ? 1u : static_cast<uint64_t>(AlignUp(pitch, 128u) >> 7u));
const auto dst_words = static_cast<uint64_t>(pitch) * height;
const bool exact_surface =
(!tail_block && width == pitch && (size & 3u) == 0 && size_words == dst_words);
const auto* x_words = tables.x_words.data();
const auto* y_words = tables.y_words.data();
if (!exact_surface) {
std::memset(dst32, 0, static_cast<size_t>(size));
}
for (uint32_t block_y = 0; block_y < height; block_y += 128u) {
const uint32_t block_height = std::min(128u, height - block_y);
for (uint32_t block_x = 0; block_x < width; block_x += 128u) {
const uint32_t block_width = std::min(128u, width - block_x);
const uint64_t block_base =
(tail_block
? 0u
: (((static_cast<uint64_t>(block_y) >> 7u) * blocks_per_row) + (block_x >> 7u))
<< 14u);
const bool full_block =
(!tail_block && block_width == 128u && block_height == 128u &&
block_base + 16384u <= src_size_words &&
(static_cast<uint64_t>(block_y + 127u) * pitch) + block_x + 127u < size_words);
if (full_block) {
for (uint32_t local_y = 0; local_y < 128u; local_y++) {
const uint64_t dst_row =
(static_cast<uint64_t>(block_y + local_y) * pitch) + block_x;
const uint64_t src_row = block_base + y_words[src_y + local_y];
for (uint32_t local_x = 0; local_x < 128u; local_x += 4u) {
dst32[dst_row + local_x + 0u] =
src32[src_row + x_words[src_x + local_x + 0u]];
dst32[dst_row + local_x + 1u] =
src32[src_row + x_words[src_x + local_x + 1u]];
dst32[dst_row + local_x + 2u] =
src32[src_row + x_words[src_x + local_x + 2u]];
dst32[dst_row + local_x + 3u] =
src32[src_row + x_words[src_x + local_x + 3u]];
}
}
continue;
}
for (uint32_t local_y = 0; local_y < block_height; local_y++) {
const uint64_t dst_row =
(static_cast<uint64_t>(block_y + local_y) * pitch) + block_x;
const uint64_t src_row = block_base + y_words[src_y + local_y];
for (uint32_t local_x = 0; local_x < block_width; local_x++) {
const uint64_t dst_index = dst_row + local_x;
const uint64_t src_index = src_row + x_words[src_x + local_x];
if (src_index < src_size_words && dst_index < size_words) {
dst32[dst_index] = src32[src_index];
}
}
}
}
}
}
void TileConvertLinearToTiledStandard64KB32(void* dst, const void* src, uint32_t width,
uint32_t height, uint32_t pitch, uint64_t size) {
KYTY_PROFILER_FUNCTION();
const auto rows = static_cast<uint64_t>(height == 0 ? 0 : height - 1u);
const auto expected_pitch = (static_cast<uint64_t>(width) + 127u) & ~uint64_t {127u};
if (pitch != expected_pitch || size < (rows * pitch + width) * sizeof(uint32_t)) {
EXIT("invalid linear-to-Standard64KB32 conversion, dst=%p src=%p extent=%ux%u "
"pitch=%u size=0x%016" PRIx64 "\n",
dst, src, width, height, pitch, size);
}
auto* dst32 = static_cast<uint32_t*>(dst);
const auto* src32 = static_cast<const uint32_t*>(src);
const auto& tables = GetStandard64KB32Tables();
const auto blocks_per_row = (static_cast<uint64_t>(pitch) + 127u) >> 7u;
const auto block_rows = (static_cast<uint64_t>(height) + 127u) >> 7u;
if (blocks_per_row > UINT64_MAX / block_rows / 65536u ||
size != blocks_per_row * block_rows * 65536u) {
EXIT("invalid Standard64KB32 allocation, extent=%ux%u pitch=%u size=0x%016" PRIx64
" blocks=%" PRIu64 "x%" PRIu64 "\n",
width, height, pitch, size, blocks_per_row, block_rows);
}
const auto* x_words = tables.x_words.data();
const auto* y_words = tables.y_words.data();
std::memset(dst32, 0, static_cast<size_t>(size));
for (uint32_t block_y = 0; block_y < height; block_y += 128u) {
const uint32_t block_height = std::min(128u, height - block_y);
for (uint32_t block_x = 0; block_x < width; block_x += 128u) {
const uint32_t block_width = std::min(128u, width - block_x);
const uint64_t block_base =
(((static_cast<uint64_t>(block_y) >> 7u) * blocks_per_row) + (block_x >> 7u))
<< 14u;
if (block_width == 128u && block_height == 128u) {
for (uint32_t local_y = 0; local_y < 128u; local_y++) {
const uint64_t src_row =
(static_cast<uint64_t>(block_y + local_y) * pitch) + block_x;
const uint64_t dst_row = block_base + y_words[local_y];
for (uint32_t local_x = 0; local_x < 128u; local_x += 4u) {
dst32[dst_row + x_words[local_x + 0u]] = src32[src_row + local_x + 0u];
dst32[dst_row + x_words[local_x + 1u]] = src32[src_row + local_x + 1u];
dst32[dst_row + x_words[local_x + 2u]] = src32[src_row + local_x + 2u];
dst32[dst_row + x_words[local_x + 3u]] = src32[src_row + local_x + 3u];
}
}
continue;
}
for (uint32_t local_y = 0; local_y < block_height; local_y++) {
const uint64_t src_row =
(static_cast<uint64_t>(block_y + local_y) * pitch) + block_x;
const uint64_t dst_row = block_base + y_words[local_y];
for (uint32_t local_x = 0; local_x < block_width; local_x++) {
dst32[dst_row + x_words[local_x]] = src32[src_row + local_x];
}
}
}
}
}
void TileConvertTiledToLinearStandard64KB16(void* dst, const void* src, uint32_t width,
uint32_t height, uint32_t pitch, uint64_t size,
uint64_t src_size, uint32_t src_x, uint32_t src_y) {
KYTY_PROFILER_FUNCTION();
EXIT_IF(dst == nullptr || src == nullptr);
EXIT_NOT_IMPLEMENTED(pitch == 0);
const bool tail_block = (src_size != 0 && (src_x != 0 || src_y != 0 || size < src_size));
EXIT_NOT_IMPLEMENTED(width > pitch);
EXIT_NOT_IMPLEMENTED(tail_block && (src_x + width > 256u || src_y + height > 128u));
auto* dst16 = static_cast<uint16_t*>(dst);
const auto* src16 = static_cast<const uint16_t*>(src);
const auto& tables = GetStandard64KB16Tables();
const auto size_words = size >> 1u;
const auto src_size_words = (src_size != 0 ? src_size : size) >> 1u;
const auto blocks_per_row =
(tail_block ? 1u : static_cast<uint64_t>(AlignUp(pitch, 256u) >> 8u));
const auto dst_words = static_cast<uint64_t>(pitch) * height;
const bool exact_surface =
(!tail_block && width == pitch && (size & 1u) == 0 && size_words == dst_words);
const auto* x_words = tables.x_words.data();
const auto* y_words = tables.y_words.data();
if (!exact_surface) {
std::memset(dst16, 0, static_cast<size_t>(size));
}
for (uint32_t block_y = 0; block_y < height; block_y += 128u) {
const uint32_t block_height = std::min(128u, height - block_y);
for (uint32_t block_x = 0; block_x < width; block_x += 256u) {
const uint32_t block_width = std::min(256u, width - block_x);
const uint64_t block_base =
(tail_block
? 0u
: (((static_cast<uint64_t>(block_y) >> 7u) * blocks_per_row) + (block_x >> 8u))
<< 15u);
const bool full_block =
(!tail_block && block_width == 256u && block_height == 128u &&
block_base + 32768u <= src_size_words &&
(static_cast<uint64_t>(block_y + 127u) * pitch) + block_x + 255u < size_words);
if (full_block) {
for (uint32_t local_y = 0; local_y < 128u; local_y++) {
const uint64_t dst_row =
(static_cast<uint64_t>(block_y + local_y) * pitch) + block_x;
const uint64_t src_row = block_base + y_words[src_y + local_y];
for (uint32_t local_x = 0; local_x < 256u; local_x += 4u) {
dst16[dst_row + local_x + 0u] =
src16[src_row + x_words[src_x + local_x + 0u]];
dst16[dst_row + local_x + 1u] =
src16[src_row + x_words[src_x + local_x + 1u]];
dst16[dst_row + local_x + 2u] =
src16[src_row + x_words[src_x + local_x + 2u]];
dst16[dst_row + local_x + 3u] =
src16[src_row + x_words[src_x + local_x + 3u]];
}
}
continue;
}
for (uint32_t local_y = 0; local_y < block_height; local_y++) {
const uint64_t dst_row =
(static_cast<uint64_t>(block_y + local_y) * pitch) + block_x;
const uint64_t src_row = block_base + y_words[src_y + local_y];
for (uint32_t local_x = 0; local_x < block_width; local_x++) {
const uint64_t dst_index = dst_row + local_x;
const uint64_t src_index = src_row + x_words[src_x + local_x];
if (src_index < src_size_words && dst_index < size_words) {
dst16[dst_index] = src16[src_index];
}
}
}
}
}
}
static void TileConvertTiledToLinearStandard64KB8Elements(
void* dst, const void* src, uint32_t width_elements, uint32_t height_elements,
uint32_t pitch_elements, uint64_t size, uint64_t src_size, uint32_t src_x, uint32_t src_y) {
KYTY_PROFILER_FUNCTION();
EXIT_IF(dst == nullptr || src == nullptr);
EXIT_NOT_IMPLEMENTED(pitch_elements == 0);
const bool tail_block = (src_size != 0 && (src_x != 0 || src_y != 0 || size < src_size));
EXIT_NOT_IMPLEMENTED(width_elements > pitch_elements);
EXIT_NOT_IMPLEMENTED(tail_block &&
(src_x + width_elements > 256u || src_y + height_elements > 256u));
auto* dst8 = static_cast<uint8_t*>(dst);
const auto* src8 = static_cast<const uint8_t*>(src);
const auto& tables = GetStandard64KB8Tables();
const auto size_words = size;
const auto src_size_words = (src_size != 0 ? src_size : size);
const auto blocks_per_row =
(tail_block ? 1u : static_cast<uint64_t>(AlignUp(pitch_elements, 256u) >> 8u));
const auto dst_words = static_cast<uint64_t>(pitch_elements) * height_elements;
const bool exact_surface = (!tail_block && size_words == dst_words);
const auto* x_words = tables.x_words.data();
const auto* y_words = tables.y_words.data();
if (!exact_surface) {
std::memset(dst8, 0, static_cast<size_t>(size));
}
for (uint32_t block_y = 0; block_y < height_elements; block_y += 256u) {
const uint32_t block_height = std::min(256u, height_elements - block_y);
for (uint32_t block_x = 0; block_x < width_elements; block_x += 256u) {
const uint32_t block_width = std::min(256u, width_elements - block_x);
const uint64_t block_base =
(tail_block
? 0u
: (((static_cast<uint64_t>(block_y) >> 8u) * blocks_per_row) + (block_x >> 8u))
<< 16u);
const bool full_block =
(!tail_block && block_width == 256u && block_height == 256u &&
block_base + 65536u <= src_size_words &&
(static_cast<uint64_t>(block_y + 255u) * pitch_elements) + block_x + 255u <
size_words);
if (full_block) {
for (uint32_t local_y = 0; local_y < 256u; local_y++) {
const uint64_t dst_row =
(static_cast<uint64_t>(block_y + local_y) * pitch_elements) + block_x;
const uint64_t src_row = block_base + y_words[src_y + local_y];
for (uint32_t local_x = 0; local_x < 256u; local_x += 4u) {
dst8[dst_row + local_x + 0u] =
src8[src_row + x_words[src_x + local_x + 0u]];
dst8[dst_row + local_x + 1u] =
src8[src_row + x_words[src_x + local_x + 1u]];
dst8[dst_row + local_x + 2u] =
src8[src_row + x_words[src_x + local_x + 2u]];
dst8[dst_row + local_x + 3u] =
src8[src_row + x_words[src_x + local_x + 3u]];
}
}
continue;
}
for (uint32_t local_y = 0; local_y < block_height; local_y++) {
const uint64_t dst_row =
(static_cast<uint64_t>(block_y + local_y) * pitch_elements) + block_x;
const uint64_t src_row = block_base + y_words[src_y + local_y];
for (uint32_t local_x = 0; local_x < block_width; local_x++) {
const uint64_t dst_index = dst_row + local_x;
const uint64_t src_index = src_row + x_words[src_x + local_x];
if (src_index < src_size_words && dst_index < size_words) {
dst8[dst_index] = src8[src_index];
}
}
}
}
}
}
static void TileConvertTiledToLinearStandard64KB64Elements(
void* dst, const void* src, uint32_t width_elements, uint32_t height_elements,
uint32_t pitch_elements, uint64_t size, uint64_t src_size, uint32_t src_x, uint32_t src_y) {
KYTY_PROFILER_FUNCTION();
EXIT_IF(dst == nullptr || src == nullptr);
EXIT_NOT_IMPLEMENTED(pitch_elements == 0);
const bool tail_block = (src_size != 0 && (src_x != 0 || src_y != 0 || size < src_size));
EXIT_NOT_IMPLEMENTED(width_elements > pitch_elements);
EXIT_NOT_IMPLEMENTED(tail_block &&
(src_x + width_elements > 128u || src_y + height_elements > 64u));
auto* dst64 = static_cast<uint64_t*>(dst);
const auto* src64 = static_cast<const uint64_t*>(src);
const auto& tables = GetStandard64KB64Tables();
const auto size_words = size >> 3u;
const auto src_size_words = (src_size != 0 ? src_size : size) >> 3u;
const auto blocks_per_row =
(tail_block ? 1u : static_cast<uint64_t>(AlignUp(pitch_elements, 128u) >> 7u));
const auto dst_words = static_cast<uint64_t>(pitch_elements) * height_elements;
const bool exact_surface = (!tail_block && (size & 7u) == 0 && size_words == dst_words);
const auto* x_words = tables.x_words.data();
const auto* y_words = tables.y_words.data();
if (!exact_surface) {
std::memset(dst64, 0, static_cast<size_t>(size));
}
for (uint32_t block_y = 0; block_y < height_elements; block_y += 64u) {
const uint32_t block_height = std::min(64u, height_elements - block_y);
for (uint32_t block_x = 0; block_x < width_elements; block_x += 128u) {
const uint32_t block_width = std::min(128u, width_elements - block_x);
const uint64_t block_base =
(tail_block
? 0u
: (((static_cast<uint64_t>(block_y) >> 6u) * blocks_per_row) + (block_x >> 7u))
<< 13u);
const bool full_block =
(!tail_block && block_width == 128u && block_height == 64u &&
block_base + 8192u <= src_size_words &&
(static_cast<uint64_t>(block_y + 63u) * pitch_elements) + block_x + 127u <
size_words);
if (full_block) {
for (uint32_t local_y = 0; local_y < 64u; local_y++) {
const uint64_t dst_row =
(static_cast<uint64_t>(block_y + local_y) * pitch_elements) + block_x;
const uint64_t src_row = block_base + y_words[src_y + local_y];
for (uint32_t local_x = 0; local_x < 128u; local_x += 8u) {
dst64[dst_row + local_x + 0u] =
src64[src_row + x_words[src_x + local_x + 0u]];
dst64[dst_row + local_x + 1u] =
src64[src_row + x_words[src_x + local_x + 1u]];
dst64[dst_row + local_x + 2u] =
src64[src_row + x_words[src_x + local_x + 2u]];
dst64[dst_row + local_x + 3u] =
src64[src_row + x_words[src_x + local_x + 3u]];
dst64[dst_row + local_x + 4u] =
src64[src_row + x_words[src_x + local_x + 4u]];
dst64[dst_row + local_x + 5u] =
src64[src_row + x_words[src_x + local_x + 5u]];
dst64[dst_row + local_x + 6u] =
src64[src_row + x_words[src_x + local_x + 6u]];
dst64[dst_row + local_x + 7u] =
src64[src_row + x_words[src_x + local_x + 7u]];
}
}
continue;
}
for (uint32_t local_y = 0; local_y < block_height; local_y++) {
const uint64_t dst_row =
(static_cast<uint64_t>(block_y + local_y) * pitch_elements) + block_x;
const uint64_t src_row = block_base + y_words[src_y + local_y];
for (uint32_t local_x = 0; local_x < block_width; local_x++) {
const uint64_t dst_index = dst_row + local_x;
const uint64_t src_index = src_row + x_words[src_x + local_x];
if (src_index < src_size_words && dst_index < size_words) {
dst64[dst_index] = src64[src_index];
}
}
}
}
}
}
static void TileConvertLinearToTiledStandard64KB64Elements(
void* dst, const void* src, uint32_t width_elements, uint32_t height_elements,
uint32_t pitch_elements, uint64_t size, uint64_t dst_size, uint32_t dst_x, uint32_t dst_y) {
if (width_elements > pitch_elements) {
EXIT("invalid linear-to-Standard64KB64 tail conversion, extent=%ux%u pitch=%u "
"size=0x%016" PRIx64 " dst_size=0x%016" PRIx64 " dst=%u,%u\n",
width_elements, height_elements, pitch_elements, size, dst_size, dst_x, dst_y);
}
auto* dst64 = static_cast<uint64_t*>(dst);
const auto* src64 = static_cast<const uint64_t*>(src);
const auto& tables = GetStandard64KB64Tables();
const auto size_words = size >> 3u;
const auto dst_size_words = dst_size >> 3u;
for (uint32_t y = 0; y < height_elements; y++) {
const uint64_t src_row = static_cast<uint64_t>(y) * pitch_elements;
const uint64_t dst_row = tables.y_words[dst_y + y];
for (uint32_t x = 0; x < width_elements; x++) {
const uint64_t src_index = src_row + x;
const uint64_t dst_index = dst_row + tables.x_words[dst_x + x];
if (src_index < size_words && dst_index < dst_size_words) {
dst64[dst_index] = src64[src_index];
}
}
}
}
static void TileConvertTiledToLinearStandard64KB128Elements(
void* dst, const void* src, uint32_t width_elements, uint32_t height_elements,
uint32_t pitch_elements, uint64_t size, uint64_t src_size, uint32_t src_x, uint32_t src_y) {
KYTY_PROFILER_FUNCTION();
EXIT_IF(dst == nullptr || src == nullptr);
EXIT_NOT_IMPLEMENTED(pitch_elements == 0);
const bool tail_block = (src_size != 0 && (src_x != 0 || src_y != 0 || size < src_size));
EXIT_NOT_IMPLEMENTED(width_elements > pitch_elements);
EXIT_NOT_IMPLEMENTED(tail_block &&
(src_x + width_elements > 64u || src_y + height_elements > 64u));
auto* dst128 = static_cast<Uint128*>(dst);
const auto* src128 = static_cast<const Uint128*>(src);
const auto& tables = GetStandard64KB128Tables();
const auto size_words = size >> 4u;
const auto src_size_words = (src_size != 0 ? src_size : size) >> 4u;
const auto blocks_per_row =
(tail_block ? 1u : static_cast<uint64_t>(AlignUp(pitch_elements, 64u) >> 6u));
const auto dst_words = static_cast<uint64_t>(pitch_elements) * height_elements;
const bool exact_surface = (!tail_block && (size & 15u) == 0 && size_words == dst_words);
const auto* x_words = tables.x_words.data();
const auto* y_words = tables.y_words.data();
if (!exact_surface) {
std::memset(dst128, 0, static_cast<size_t>(size));
}
for (uint32_t block_y = 0; block_y < height_elements; block_y += 64u) {
const uint32_t block_height = std::min(64u, height_elements - block_y);
for (uint32_t block_x = 0; block_x < width_elements; block_x += 64u) {
const uint32_t block_width = std::min(64u, width_elements - block_x);
const uint64_t block_base =
(tail_block
? 0u
: (((static_cast<uint64_t>(block_y) >> 6u) * blocks_per_row) + (block_x >> 6u))
<< 12u);
const bool full_block =
(!tail_block && block_width == 64u && block_height == 64u &&
block_base + 4096u <= src_size_words &&
(static_cast<uint64_t>(block_y + 63u) * pitch_elements) + block_x + 63u <
size_words);
if (full_block) {
for (uint32_t local_y = 0; local_y < 64u; local_y++) {
const uint64_t dst_row =
(static_cast<uint64_t>(block_y + local_y) * pitch_elements) + block_x;
const uint64_t src_row = block_base + y_words[src_y + local_y];
for (uint32_t local_x = 0; local_x < 64u; local_x += 4u) {
dst128[dst_row + local_x + 0u] =
src128[src_row + x_words[src_x + local_x + 0u]];
dst128[dst_row + local_x + 1u] =
src128[src_row + x_words[src_x + local_x + 1u]];
dst128[dst_row + local_x + 2u] =
src128[src_row + x_words[src_x + local_x + 2u]];
dst128[dst_row + local_x + 3u] =
src128[src_row + x_words[src_x + local_x + 3u]];
}
}
continue;
}
for (uint32_t local_y = 0; local_y < block_height; local_y++) {
const uint64_t dst_row =
(static_cast<uint64_t>(block_y + local_y) * pitch_elements) + block_x;
const uint64_t src_row = block_base + y_words[src_y + local_y];
for (uint32_t local_x = 0; local_x < block_width; local_x++) {
const uint64_t dst_index = dst_row + local_x;
const uint64_t src_index = src_row + x_words[src_x + local_x];
if (src_index < src_size_words && dst_index < size_words) {
dst128[dst_index] = src128[src_index];
}
}
}
}
}
}
void TileConvertTiledToLinearStandard64KB(void* dst, const void* src, uint32_t format,
uint32_t width, uint32_t height, uint32_t pitch,
uint64_t size, uint64_t src_size, uint32_t src_x,
uint32_t src_y) {
uint32_t bytes_per_element = 0;
uint32_t texels_per_element_wide = 0;
uint32_t texels_per_element_tall = 0;
uint32_t block_width_log2 = 0;
uint32_t block_height_log2 = 0;
EXIT_NOT_IMPLEMENTED(!Gen5Standard64KBLayout(format, &bytes_per_element,
&texels_per_element_wide, &texels_per_element_tall,
&block_width_log2, &block_height_log2));
const uint32_t width_elements =
std::max((width + texels_per_element_wide - 1u) / texels_per_element_wide, 1u);
const uint32_t height_elements =
std::max((height + texels_per_element_tall - 1u) / texels_per_element_tall, 1u);
const uint32_t pitch_elements =
std::max((pitch + texels_per_element_wide - 1u) / texels_per_element_wide, 1u);
switch (bytes_per_element) {
case 1:
TileConvertTiledToLinearStandard64KB8Elements(dst, src, width_elements, height_elements,
pitch_elements, size, src_size, src_x,
src_y);
break;
case 2:
TileConvertTiledToLinearStandard64KB16(dst, src, width_elements, height_elements,
pitch_elements, size, src_size, src_x, src_y);
break;
case 4:
TileConvertTiledToLinearStandard64KB32(dst, src, width_elements, height_elements,
pitch_elements, size, src_size, src_x, src_y);
break;
case 8:
TileConvertTiledToLinearStandard64KB64Elements(dst, src, width_elements,
height_elements, pitch_elements, size,
src_size, src_x, src_y);
break;
case 16:
TileConvertTiledToLinearStandard64KB128Elements(dst, src, width_elements,
height_elements, pitch_elements, size,
src_size, src_x, src_y);
break;
default: EXIT("unsupported Standard64KB element size: %u\n", bytes_per_element);
}
}
void TileConvertTiledToLinearStandard256B(void* dst, const void* src, uint32_t format,
uint32_t width, uint32_t height, uint32_t pitch,
uint64_t dst_size, uint64_t src_size) {
uint32_t bytes_per_element = 0;
uint32_t texels_per_element_wide = 0;
uint32_t texels_per_element_tall = 0;
uint32_t block_width_log2 = 0;
uint32_t block_height_log2 = 0;
EXIT_NOT_IMPLEMENTED(!Gen5Standard256BLayout(format, &bytes_per_element,
&texels_per_element_wide, &texels_per_element_tall,
&block_width_log2, &block_height_log2));
const uint32_t row_elements =
std::max((pitch + texels_per_element_wide - 1u) / texels_per_element_wide, 1u);
const uint32_t width_elements =
std::max((width + texels_per_element_wide - 1u) / texels_per_element_wide, 1u);
const uint32_t height_elements =
std::max((height + texels_per_element_tall - 1u) / texels_per_element_tall, 1u);
const uint32_t padded_width = AlignUp(row_elements, 1u << block_width_log2);
if (src_size == 0) {
src_size = dst_size;
}
switch (bytes_per_element) {
case 1:
DetileStandard256BTyped(static_cast<uint8_t*>(dst), static_cast<const uint8_t*>(src),
row_elements, width_elements, height_elements, padded_width,
block_width_log2, block_height_log2, dst_size, src_size);
break;
case 2:
DetileStandard256BTyped(static_cast<uint16_t*>(dst), static_cast<const uint16_t*>(src),
row_elements, width_elements, height_elements, padded_width,
block_width_log2, block_height_log2, dst_size, src_size);
break;
case 4:
DetileStandard256BTyped(static_cast<uint32_t*>(dst), static_cast<const uint32_t*>(src),
row_elements, width_elements, height_elements, padded_width,
block_width_log2, block_height_log2, dst_size, src_size);
break;
case 8:
DetileStandard256BTyped(static_cast<uint64_t*>(dst), static_cast<const uint64_t*>(src),
row_elements, width_elements, height_elements, padded_width,
block_width_log2, block_height_log2, dst_size, src_size);
break;
case 16:
DetileStandard256BTyped(static_cast<Uint128*>(dst), static_cast<const Uint128*>(src),
row_elements, width_elements, height_elements, padded_width,
block_width_log2, block_height_log2, dst_size, src_size);
break;
default: EXIT("unsupported Standard256B element size: %u\n", bytes_per_element);
}
}
// Prospero depth/stencil-tile address table for 2D, 1xAA surfaces by element size.
static constexpr uint32_t Depth64KB8XOffsetBytes(uint32_t x) {
uint32_t offset = 0;
offset ^= x & 0x0001u;
offset ^= (x << 1u) & 0x0004u;
offset ^= (x << 2u) & 0x0010u;
offset ^= (x << 3u) & 0x0040u;
offset ^= (x << 5u) & 0x0300u;
offset ^= (x << 4u) & 0x0400u;
offset ^= (x << 6u) & 0x0800u;
offset ^= (x << 7u) & 0x2000u;
offset ^= (x << 8u) & 0x8000u;
return offset;
}
static constexpr uint32_t Depth64KB8YOffsetBytes(uint32_t y) {
uint32_t offset = 0;
offset ^= (y << 1u) & 0x0002u;
offset ^= (y << 2u) & 0x0008u;
offset ^= (y << 3u) & 0x00a0u;
offset ^= (y << 5u) & 0x0f00u;
offset ^= (y << 6u) & 0x1000u;
offset ^= (y << 7u) & 0x4000u;
return offset;
}
static constexpr uint32_t Depth64KB16XOffsetBytes(uint32_t x) {
uint32_t offset = 0;
offset ^= (x << 1u) & 0x0002u;
offset ^= (x << 2u) & 0x0008u;
offset ^= (x << 3u) & 0x0020u;
offset ^= (x << 4u) & 0x0480u;
offset ^= (x << 5u) & 0x0300u;
offset ^= (x << 6u) & 0x0800u;
offset ^= (x << 7u) & 0x2000u;
offset ^= (x << 8u) & 0x8000u;
return offset;
}
static constexpr uint32_t Depth64KB16YOffsetBytes(uint32_t y) {
uint32_t offset = 0;
offset ^= (y << 2u) & 0x0004u;
offset ^= (y << 3u) & 0x0010u;
offset ^= (y << 4u) & 0x0040u;
offset ^= (y << 5u) & 0x0f00u;
offset ^= (y << 8u) & 0x5000u;
return offset;
}
static constexpr uint32_t Depth64KB32XOffsetBytes(uint32_t x) {
uint32_t offset = 0;
offset ^= (x << 2u) & 0x0004u;
offset ^= (x << 3u) & 0x0010u;
offset ^= (x << 4u) & 0x0440u;
offset ^= (x << 5u) & 0x0300u;
offset ^= (x << 6u) & 0x0800u;
offset ^= (x << 9u) & 0xa000u;
return offset;
}
static constexpr uint32_t Depth64KB32YOffsetBytes(uint32_t y) {
uint32_t offset = 0;
offset ^= (y << 3u) & 0x0008u;
offset ^= (y << 4u) & 0x0020u;
offset ^= (y << 5u) & 0x0f80u;
offset ^= (y << 9u) & 0x1000u;
offset ^= (y << 8u) & 0x4000u;
return offset;
}
static_assert(Depth64KB8XOffsetBytes(2) == 0x0004u);
static_assert(Depth64KB8YOffsetBytes(1) == 0x0002u);
static_assert((Depth64KB8XOffsetBytes(3) ^ Depth64KB8YOffsetBytes(5)) == 0x0027u);
static_assert(Depth64KB16XOffsetBytes(2) == 0x0008u);
static_assert(Depth64KB16YOffsetBytes(1) == 0x0004u);
static_assert((Depth64KB16XOffsetBytes(3) ^ Depth64KB16YOffsetBytes(5)) == 0x004eu);
static_assert(Depth64KB32XOffsetBytes(2) == 0x0010u);
static_assert(Depth64KB32YOffsetBytes(1) == 0x0008u);
static_assert((Depth64KB32XOffsetBytes(3) ^ Depth64KB32YOffsetBytes(5)) == 0x009cu);
struct Depth64KB8Tables {
std::array<uint32_t, 256> x_words {};
std::array<uint32_t, 256> y_words {};
constexpr Depth64KB8Tables() {
for (uint32_t x = 0; x < x_words.size(); x++) {
x_words[x] = Depth64KB8XOffsetBytes(x);
}
for (uint32_t y = 0; y < y_words.size(); y++) {
y_words[y] = Depth64KB8YOffsetBytes(y);
}
}
};
struct Depth64KB16Tables {
std::array<uint32_t, 256> x_words {};
std::array<uint32_t, 128> y_words {};
constexpr Depth64KB16Tables() {
for (uint32_t x = 0; x < x_words.size(); x++) {
x_words[x] = Depth64KB16XOffsetBytes(x) / sizeof(uint16_t);
}
for (uint32_t y = 0; y < y_words.size(); y++) {
y_words[y] = Depth64KB16YOffsetBytes(y) / sizeof(uint16_t);
}
}
};
struct Depth64KB32Tables {
std::array<uint32_t, 128> x_words {};
std::array<uint32_t, 128> y_words {};
constexpr Depth64KB32Tables() {
for (uint32_t x = 0; x < x_words.size(); x++) {
x_words[x] = Depth64KB32XOffsetBytes(x) / sizeof(uint32_t);
}
for (uint32_t y = 0; y < y_words.size(); y++) {
y_words[y] = Depth64KB32YOffsetBytes(y) / sizeof(uint32_t);
}
}
};
template <bool tiled_to_linear, typename T, size_t x_count, size_t y_count>
static void TileConvertDepthTyped(void* dst, const void* src, uint32_t width, uint32_t height,
uint32_t pitch, uint64_t size,
const std::array<uint32_t, x_count>& x_words,
const std::array<uint32_t, y_count>& y_words) {
constexpr uint32_t block_width = static_cast<uint32_t>(x_count);
constexpr uint32_t block_height = static_cast<uint32_t>(y_count);
const uint32_t block_columns = 1u + (width - 1u) / block_width;
const uint64_t blocks_per_row = pitch / block_width;
const uint32_t block_rows = 1u + (height - 1u) / block_height;
struct ConvertParams {
uint32_t start_block_row;
uint32_t end_block_row;
uint32_t width;
uint32_t height;
uint32_t pitch;
uint32_t block_columns;
uint64_t blocks_per_row;
const uint32_t* x_words;
const uint32_t* y_words;
T* dst;
const T* src;
};
auto convert = [](void* args) {
auto* p = static_cast<ConvertParams*>(args);
for (uint32_t block_row = p->start_block_row; block_row < p->end_block_row; block_row++) {
const uint32_t block_y = block_row * static_cast<uint32_t>(y_count);
const uint32_t copy_height =
std::min(static_cast<uint32_t>(y_count), p->height - block_y);
for (uint32_t block_column = 0; block_column < p->block_columns; block_column++) {
const uint32_t block_x = block_column * static_cast<uint32_t>(x_count);
const uint32_t copy_width =
std::min(static_cast<uint32_t>(x_count), p->width - block_x);
const uint64_t block_base =
(static_cast<uint64_t>(block_row) * p->blocks_per_row + block_column) *
(65536u / sizeof(T));
for (uint32_t y = 0; y < copy_height; y++) {
const uint64_t linear_row =
static_cast<uint64_t>(block_y + y) * p->pitch + block_x;
const uint64_t tiled_row = block_base + p->y_words[y];
for (uint32_t x = 0; x < copy_width; x++) {
const uint64_t linear_index = linear_row + x;
const uint64_t tiled_index = tiled_row ^ p->x_words[x];
if constexpr (tiled_to_linear) {
p->dst[linear_index] = p->src[tiled_index];
} else {
p->dst[tiled_index] = p->src[linear_index];
}
}
}
}
}
};
auto* dst_typed = static_cast<T*>(dst);
const auto* src_typed = static_cast<const T*>(src);
std::memset(dst_typed, 0, static_cast<size_t>(size));
const uint32_t split = block_rows / 2u;
ConvertParams first {0,
split,
width,
height,
pitch,
block_columns,
blocks_per_row,
x_words.data(),
y_words.data(),
dst_typed,
src_typed};
ConvertParams second {split, block_rows, width, height,
pitch, block_columns, blocks_per_row, x_words.data(),
y_words.data(), dst_typed, src_typed};
if (split == 0) {
convert(&second);
return;
}
EXIT_IF(g_tiler == nullptr);
Common::LockGuard lock(g_tiler->m_mutex);
g_tiler->m_job1.Execute([convert, &second] { convert(&second); });
convert(&first);
g_tiler->m_job1.Wait();
}
template <bool tiled_to_linear>
static void TileConvertDepth(void* dst, const void* src, uint32_t format, uint32_t width,
uint32_t height, uint32_t pitch, uint64_t size) {
KYTY_PROFILER_FUNCTION();
const uint32_t bytes_per_element = Prospero::NumBytesPerElement(format);
const bool supported_bpe =
bytes_per_element == 1 || bytes_per_element == 2 || bytes_per_element == 4;
const uint32_t block_width = supported_bpe && bytes_per_element <= 2 ? 256u : 128u;
const uint32_t block_height = supported_bpe ? 65536u / (block_width * bytes_per_element) : 0;
if (dst == nullptr || src == nullptr || width == 0 || height == 0 || pitch < width ||
!supported_bpe || pitch % block_width != 0 ||
size == 0 || size % 65536u != 0) {
EXIT("unsupported depth conversion, dst=%p src=%p format=%u "
"extent=%ux%u pitch=%u size=0x%016" PRIx64 "\n",
dst, src, format, width, height, pitch, size);
}
const uint64_t block_rows =
(static_cast<uint64_t>(height) + block_height - 1u) / block_height;
const uint64_t blocks_per_row = pitch / block_width;
if (blocks_per_row > UINT64_MAX / block_rows ||
blocks_per_row * block_rows > UINT64_MAX / 65536u ||
size != blocks_per_row * block_rows * 65536u) {
EXIT("depth storage disagrees with 64 KiB block layout, "
"extent=%ux%u pitch=%u bpe=%u size=0x%016" PRIx64 "\n",
width, height, pitch, bytes_per_element, size);
}
if (bytes_per_element == 1) {
static constexpr Depth64KB8Tables tables;
TileConvertDepthTyped<tiled_to_linear, uint8_t>(dst, src, width, height, pitch, size,
tables.x_words, tables.y_words);
} else if (bytes_per_element == 2) {
static constexpr Depth64KB16Tables tables;
TileConvertDepthTyped<tiled_to_linear, uint16_t>(dst, src, width, height, pitch, size,
tables.x_words, tables.y_words);
} else {
static constexpr Depth64KB32Tables tables;
TileConvertDepthTyped<tiled_to_linear, uint32_t>(dst, src, width, height, pitch, size,
tables.x_words, tables.y_words);
}
}
void TileConvertTiledToLinearDepth(void* dst, const void* src, uint32_t format, uint32_t width,
uint32_t height, uint32_t pitch, uint64_t size) {
TileConvertDepth<true>(dst, src, format, width, height, pitch, size);
}
void TileConvertLinearToTiledDepth(void* dst, const void* src, uint32_t format, uint32_t width,
uint32_t height, uint32_t pitch, uint64_t size) {
TileConvertDepth<false>(dst, src, format, width, height, pitch, size);
}
void TileConvertTiledToLinearStandard4KB(void* dst, const void* src, uint32_t format,
uint32_t width, uint32_t height, uint32_t pitch,
uint64_t dst_size, uint64_t src_size, uint32_t src_x,
uint32_t src_y) {
KYTY_PROFILER_FUNCTION();
EXIT_IF(dst == nullptr);
EXIT_IF(src == nullptr);
EXIT_NOT_IMPLEMENTED(pitch == 0);
EXIT_NOT_IMPLEMENTED(width > pitch);
uint32_t bytes_per_element = 0;
uint32_t texels_per_element_wide = 0;
uint32_t texels_per_element_tall = 0;
uint32_t block_width_log2 = 0;
uint32_t block_height_log2 = 0;
EXIT_NOT_IMPLEMENTED(!Gen5Standard4KBLayout(format, &bytes_per_element,
&texels_per_element_wide, &texels_per_element_tall,
&block_width_log2, &block_height_log2));
const uint32_t row_elements =
std::max((pitch + texels_per_element_wide - 1u) / texels_per_element_wide, 1u);
const uint32_t width_elements =
std::max((width + texels_per_element_wide - 1u) / texels_per_element_wide, 1u);
const uint32_t height_elements =
std::max((height + texels_per_element_tall - 1u) / texels_per_element_tall, 1u);
const uint32_t block_width = 1u << block_width_log2;
const uint32_t block_height = 1u << block_height_log2;
const uint32_t padded_width = (row_elements + block_width - 1u) & ~(block_width - 1u);
const uint32_t padded_height = (height_elements + block_height - 1u) & ~(block_height - 1u);
EXIT_IF((padded_width & (block_width - 1u)) != 0);
EXIT_IF((padded_height & (block_height - 1u)) != 0);
EXIT_NOT_IMPLEMENTED(src_x >= block_width || src_y >= block_height);
EXIT_NOT_IMPLEMENTED(src_x != 0 && src_x + width_elements > block_width);
EXIT_NOT_IMPLEMENTED(src_y != 0 && src_y + height_elements > block_height);
auto* dst8 = static_cast<uint8_t*>(dst);
std::memset(dst8, 0, static_cast<size_t>(dst_size));
switch (bytes_per_element) {
case 1:
DetileStandard4KBTyped(static_cast<uint8_t*>(dst), static_cast<const uint8_t*>(src),
row_elements, width_elements, height_elements, padded_width,
block_width_log2, block_height_log2, dst_size, src_size, src_x,
src_y);
break;
case 2:
DetileStandard4KBTyped(static_cast<uint16_t*>(dst), static_cast<const uint16_t*>(src),
row_elements, width_elements, height_elements, padded_width,
block_width_log2, block_height_log2, dst_size, src_size, src_x,
src_y);
break;
case 4:
DetileStandard4KBTyped(static_cast<uint32_t*>(dst), static_cast<const uint32_t*>(src),
row_elements, width_elements, height_elements, padded_width,
block_width_log2, block_height_log2, dst_size, src_size, src_x,
src_y);
break;
case 8:
DetileStandard4KBTyped(static_cast<uint64_t*>(dst), static_cast<const uint64_t*>(src),
row_elements, width_elements, height_elements, padded_width,
block_width_log2, block_height_log2, dst_size, src_size, src_x,
src_y);
break;
case 16:
DetileStandard4KBTyped(static_cast<Uint128*>(dst), static_cast<const Uint128*>(src),
row_elements, width_elements, height_elements, padded_width,
block_width_log2, block_height_log2, dst_size, src_size, src_x,
src_y);
break;
default: EXIT("unsupported Standard4KB element size: %u\n", bytes_per_element);
}
}
void TileConvertTiledToLinearStandard4KB3D(void* dst, const void* src, uint32_t format,
uint32_t width, uint32_t height, uint32_t depth,
uint32_t pitch, uint64_t dst_slice_stride,
uint64_t dst_size, uint64_t src_size, bool clear_dst) {
KYTY_PROFILER_FUNCTION();
EXIT_IF(dst == nullptr);
EXIT_IF(src == nullptr);
EXIT_NOT_IMPLEMENTED(pitch == 0);
EXIT_NOT_IMPLEMENTED(width > pitch);
EXIT_NOT_IMPLEMENTED(depth == 0);
uint32_t bytes_per_element = 0;
uint32_t texels_per_element_wide = 0;
uint32_t texels_per_element_tall = 0;
uint32_t block_width_log2 = 0;
uint32_t block_height_log2 = 0;
uint32_t block_depth_log2 = 0;
EXIT_NOT_IMPLEMENTED(!TileGetStandard4KBVolumeLayout(
format, &bytes_per_element, &texels_per_element_wide, &texels_per_element_tall,
&block_width_log2, &block_height_log2, &block_depth_log2));
EXIT_NOT_IMPLEMENTED(dst_slice_stride == 0);
EXIT_NOT_IMPLEMENTED((dst_slice_stride % bytes_per_element) != 0);
const uint32_t row_elements =
std::max((pitch + texels_per_element_wide - 1u) / texels_per_element_wide, 1u);
const uint32_t width_elements =
std::max((width + texels_per_element_wide - 1u) / texels_per_element_wide, 1u);
const uint32_t height_elements =
std::max((height + texels_per_element_tall - 1u) / texels_per_element_tall, 1u);
const uint32_t block_width = 1u << block_width_log2;
const uint32_t block_height = 1u << block_height_log2;
const uint32_t padded_width = AlignUp(row_elements, block_width);
const uint32_t padded_height = AlignUp(height_elements, block_height);
src_size = (src_size != 0 ? src_size : dst_size);
if (clear_dst) {
std::memset(dst, 0, static_cast<size_t>(dst_size));
}
switch (bytes_per_element) {
case 1:
DetileStandard4KBVolumeTyped(static_cast<uint8_t*>(dst),
static_cast<const uint8_t*>(src), row_elements,
width_elements, height_elements, depth, padded_width,
padded_height, block_width_log2, block_height_log2,
block_depth_log2, dst_slice_stride, dst_size, src_size);
break;
case 2:
DetileStandard4KBVolumeTyped(static_cast<uint16_t*>(dst),
static_cast<const uint16_t*>(src), row_elements,
width_elements, height_elements, depth, padded_width,
padded_height, block_width_log2, block_height_log2,
block_depth_log2, dst_slice_stride, dst_size, src_size);
break;
case 4:
DetileStandard4KBVolumeTyped(static_cast<uint32_t*>(dst),
static_cast<const uint32_t*>(src), row_elements,
width_elements, height_elements, depth, padded_width,
padded_height, block_width_log2, block_height_log2,
block_depth_log2, dst_slice_stride, dst_size, src_size);
break;
case 8:
DetileStandard4KBVolumeTyped(static_cast<uint64_t*>(dst),
static_cast<const uint64_t*>(src), row_elements,
width_elements, height_elements, depth, padded_width,
padded_height, block_width_log2, block_height_log2,
block_depth_log2, dst_slice_stride, dst_size, src_size);
break;
case 16:
DetileStandard4KBVolumeTyped(static_cast<Uint128*>(dst),
static_cast<const Uint128*>(src), row_elements,
width_elements, height_elements, depth, padded_width,
padded_height, block_width_log2, block_height_log2,
block_depth_log2, dst_slice_stride, dst_size, src_size);
break;
default: EXIT("unsupported Standard4KB volume element size: %u\n", bytes_per_element);
}
}
void TileConvertTiledToLinear(void* dst, const void* src, TileMode mode, uint32_t width,
uint32_t height) {
KYTY_PROFILER_FUNCTION();
EXIT_NOT_IMPLEMENTED(mode != TileMode::VideoOutTiled);
Tiler32 t;
t.Init(width, height);
Detile32(t, width, height, width, static_cast<uint8_t*>(dst), static_cast<const uint8_t*>(src));
}
void TileConvertTiledToLinearRenderTarget(void* dst, const void* src, uint32_t width,
uint32_t height, uint32_t pitch,
uint32_t bytes_per_element, uint64_t size,
uint64_t src_size, uint32_t src_x, uint32_t src_y) {
KYTY_PROFILER_FUNCTION();
EXIT_IF(dst == nullptr || src == nullptr);
EXIT_NOT_IMPLEMENTED(width > pitch);
EXIT_NOT_IMPLEMENTED(pitch == 0);
src_size = (src_size != 0 ? src_size : size);
const bool tail_block = (src_size != 0 && (src_x != 0 || src_y != 0 || size < src_size));
if (!tail_block && bytes_per_element <= 8) {
switch (bytes_per_element) {
case 1:
ConvertRenderTargetTyped<uint8_t, true>(width, height, pitch,
static_cast<uint8_t*>(dst),
static_cast<const uint8_t*>(src), size);
break;
case 2:
ConvertRenderTargetTyped<uint16_t, true>(width, height, pitch,
static_cast<uint16_t*>(dst),
static_cast<const uint16_t*>(src), size);
break;
case 4:
ConvertRenderTargetTyped<uint32_t, true>(width, height, pitch,
static_cast<uint32_t*>(dst),
static_cast<const uint32_t*>(src), size);
break;
case 8:
ConvertRenderTargetTyped<uint64_t, true>(width, height, pitch,
static_cast<uint64_t*>(dst),
static_cast<const uint64_t*>(src), size);
break;
default: EXIT("unsupported render-target element size: %u\n", bytes_per_element);
}
return;
}
switch (bytes_per_element) {
case 1:
TileConvertTiledToLinearStandard64KB8Elements(dst, src, width, height, pitch, size,
src_size, src_x, src_y);
break;
case 2:
TileConvertTiledToLinearStandard64KB16(dst, src, width, height, pitch, size, src_size,
src_x, src_y);
break;
case 4:
TileConvertTiledToLinearStandard64KB32(dst, src, width, height, pitch, size, src_size,
src_x, src_y);
break;
case 8:
TileConvertTiledToLinearStandard64KB64Elements(dst, src, width, height, pitch, size,
src_size, src_x, src_y);
break;
case 16:
TileConvertTiledToLinearStandard64KB128Elements(dst, src, width, height, pitch, size,
src_size, src_x, src_y);
break;
default: EXIT("unsupported render-target element size: %u\n", bytes_per_element);
}
}
void TileConvertLinearToTiledRenderTarget(void* dst, const void* src, uint32_t width,
uint32_t height, uint32_t pitch,
uint32_t bytes_per_element, uint64_t size,
uint64_t dst_size, uint32_t dst_x, uint32_t dst_y) {
KYTY_PROFILER_FUNCTION();
const bool tail_block = dst_size != 0 && (dst_x != 0 || dst_y != 0 || size < dst_size);
if (tail_block) {
//Not sure about this at all
if (bytes_per_element != 8) {
EXIT("unsupported linear-to-tiled render-target tail element size: %u\n",
bytes_per_element);
}
TileConvertLinearToTiledStandard64KB64Elements(dst, src, width, height, pitch, size,
dst_size, dst_x, dst_y);
return;
}
if (dst == nullptr || src == nullptr || width == 0 || height == 0 || pitch == 0 ||
width > pitch || bytes_per_element == 0 || size == 0 || size % bytes_per_element != 0) {
EXIT("invalid linear-to-tiled render-target conversion, dst=%p src=%p extent=%ux%u "
"pitch=%u bpe=%u size=0x%016" PRIx64 "\n",
dst, src, width, height, pitch, bytes_per_element, size);
}
const auto rows = static_cast<uint64_t>(height - 1);
if (rows > (UINT64_MAX - width) / pitch ||
(rows * pitch + width) > UINT64_MAX / bytes_per_element ||
size < (rows * pitch + width) * bytes_per_element) {
EXIT("linear-to-tiled render-target storage is too small, extent=%ux%u pitch=%u "
"bpe=%u size=0x%016" PRIx64 "\n",
width, height, pitch, bytes_per_element, size);
}
std::memset(dst, 0, size);
switch (bytes_per_element) {
case 1:
ConvertRenderTargetTyped<uint8_t, false>(width, height, pitch,
static_cast<uint8_t*>(dst),
static_cast<const uint8_t*>(src), size);
break;
case 2:
ConvertRenderTargetTyped<uint16_t, false>(width, height, pitch,
static_cast<uint16_t*>(dst),
static_cast<const uint16_t*>(src), size);
break;
case 4:
ConvertRenderTargetTyped<uint32_t, false>(width, height, pitch,
static_cast<uint32_t*>(dst),
static_cast<const uint32_t*>(src), size);
break;
case 8:
ConvertRenderTargetTyped<uint64_t, false>(width, height, pitch,
static_cast<uint64_t*>(dst),
static_cast<const uint64_t*>(src), size);
break;
default: EXIT("unsupported render-target element size: %u\n", bytes_per_element);
}
}
bool TileGetDepthSize(uint32_t width, uint32_t height, uint32_t pitch, uint32_t z_format,
uint32_t stencil_format, bool htile, TileSizeAlign* stencil_size,
TileSizeAlign* htile_size, TileSizeAlign* depth_size) {
EXIT_IF(pitch != 0);
// Prospero derives uncompressed depth/stencil as independent 64 KiB block surfaces and HTile
// as 32 KiB metadata blocks covering 1024x512 pixels for a single-mip, single-slice target.
if (width > 0 && width <= 16384 && height > 0 && height <= 16384 &&
(z_format == 1 || z_format == 3) && stencil_format <= 1) {
const uint32_t depth_bytes = z_format == 1 ? 2u : 4u;
const uint32_t depth_block_width = z_format == 1 ? 256u : 128u;
const uint64_t depth_bytes_total =
static_cast<uint64_t>(AlignUp(width, depth_block_width)) * AlignUp(height, 128u) *
depth_bytes;
const uint64_t stencil_bytes_total =
stencil_format == 1
? static_cast<uint64_t>(AlignUp(width, 256u)) * AlignUp(height, 256u)
: 0;
const uint64_t htile_bytes_total =
htile ? static_cast<uint64_t>(AlignUp(width, 1024u) / 1024u) *
(AlignUp(height, 512u) / 512u) * 32768u
: 0;
if (depth_bytes_total <= UINT32_MAX && stencil_bytes_total <= UINT32_MAX &&
htile_bytes_total <= UINT32_MAX) {
*depth_size = {static_cast<uint32_t>(depth_bytes_total), 65536};
*stencil_size = stencil_format == 1
? TileSizeAlign {static_cast<uint32_t>(stencil_bytes_total), 65536}
: TileSizeAlign {};
*htile_size = htile ? TileSizeAlign {static_cast<uint32_t>(htile_bytes_total), 32768}
: TileSizeAlign {};
return true;
}
}
*depth_size = TileSizeAlign();
*htile_size = TileSizeAlign();
*stencil_size = TileSizeAlign();
return false;
}
uint32_t TileGetRenderTargetPitch(uint32_t width, uint32_t bytes_per_element) {
uint32_t block_width = 0;
uint32_t block_height = 0;
if (width == 0 || !Gen5Thin64KBBlockSizeFromElementBytes(bytes_per_element, &block_width,
&block_height)) {
return 0;
}
const uint64_t pitch = (static_cast<uint64_t>(width) + block_width - 1u) &
~static_cast<uint64_t>(block_width - 1u);
return pitch <= UINT32_MAX ? static_cast<uint32_t>(pitch) : 0;
}
bool TileGetRenderTargetSize(uint32_t width, uint32_t height, uint32_t pitch,
uint32_t bytes_per_element, TileSizeAlign* total_size) {
*total_size = {};
uint32_t block_width = 0;
uint32_t block_height = 0;
if (height == 0 || pitch == 0 ||
!Gen5Thin64KBBlockSizeFromElementBytes(bytes_per_element, &block_width,
&block_height) ||
pitch != TileGetRenderTargetPitch(width, bytes_per_element)) {
return false;
}
const uint64_t padded_height = (static_cast<uint64_t>(height) + block_height - 1u) &
~static_cast<uint64_t>(block_height - 1u);
const uint64_t size = static_cast<uint64_t>(pitch) * padded_height * bytes_per_element;
if (size == 0 || size > UINT32_MAX) {
return false;
}
total_size->size = static_cast<uint32_t>(size);
total_size->align = 65536;
return true;
}
bool TileGetRenderTargetMipLayout(uint32_t width, uint32_t height, uint32_t pitch,
uint32_t bytes_per_element, uint32_t levels,
TileSizeAlign* total_size, TileSizeOffset* level_sizes,
TilePaddedSize* padded_size) {
*total_size = {};
if (width == 0 || height == 0 || levels == 0 || levels > 16 ||
pitch != TileGetRenderTargetPitch(width, bytes_per_element)) {
return false;
}
uint32_t max_levels = 1;
uint32_t max_dimension = std::max(width, height);
while (max_dimension > 1) {
max_dimension >>= 1u;
max_levels++;
}
if (levels > max_levels) {
return false;
}
uint32_t format = 0;
switch (bytes_per_element) {
case 1: format = Prospero::GpuEnumValue(Prospero::BufferFormat::k8UNorm); break;
case 2: format = Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm); break;
case 4: format = Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float); break;
case 8: format = Prospero::GpuEnumValue(Prospero::BufferFormat::k16_16_16_16Float); break;
case 16: format = Prospero::GpuEnumValue(Prospero::BufferFormat::k32_32_32_32Float); break;
default: return false;
}
TileGetTextureSize(format, width, height, pitch, levels,
Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget), total_size, level_sizes,
padded_size);
return total_size->size != 0 && total_size->align == 65536;
}
void TileGetTextureSize(uint32_t format, uint32_t width, uint32_t height, uint32_t pitch,
uint32_t levels, uint32_t tile, TileSizeAlign* total_size,
TileSizeOffset* level_sizes, TilePaddedSize* padded_size) {
KYTY_PROFILER_FUNCTION();
EXIT_IF(levels == 0 || levels > 16);
if (const uint32_t bytes_per_element = Prospero::NumBytesPerElement(format);
bytes_per_element != 0 && tile == 0) {
uint32_t mip_pitch[16] {};
uint32_t mip_height[16] {};
uint32_t mip_size[16] {};
for (uint32_t l = 0; l < levels; l++) {
mip_pitch[l] = CalcLinearAlignedLevelPitch(width, height, l, bytes_per_element,
&mip_height[l], &mip_size[l]);
}
const uint32_t total = SetLinearMipChainLayout(levels, mip_pitch, mip_height, mip_size,
level_sizes, padded_size);
if (total_size != nullptr) {
total_size->size = total;
total_size->align = 256;
}
return;
}
uint32_t std256_bytes_per_element = 0;
uint32_t std256_texels_per_element_wide = 0;
uint32_t std256_texels_per_element_tall = 0;
uint32_t std256_block_width_log2 = 0;
uint32_t std256_block_height_log2 = 0;
if (tile == 1 &&
Gen5Standard256BLayout(format, &std256_bytes_per_element, &std256_texels_per_element_wide,
&std256_texels_per_element_tall, &std256_block_width_log2,
&std256_block_height_log2)) {
uint32_t offset = 0;
uint32_t mip_pitch = pitch;
uint32_t mip_height = height;
for (uint32_t l = 0; l < levels; l++) {
const uint32_t row_elements = std::max(
(mip_pitch + std256_texels_per_element_wide - 1u) / std256_texels_per_element_wide,
1u);
const uint32_t height_elements = std::max(
(mip_height + std256_texels_per_element_tall - 1u) / std256_texels_per_element_tall,
1u);
const uint32_t padded_width = AlignUp(row_elements, 1u << std256_block_width_log2);
const uint32_t padded_height = AlignUp(height_elements, 1u << std256_block_height_log2);
const uint32_t size = padded_width * padded_height * std256_bytes_per_element;
if (level_sizes != nullptr) {
level_sizes[l].size = size;
level_sizes[l].offset = offset;
}
if (padded_size != nullptr) {
padded_size[l].width = padded_width * std256_texels_per_element_wide;
padded_size[l].height = padded_height * std256_texels_per_element_tall;
}
offset += size;
mip_pitch = std::max(mip_pitch / 2u, 1u);
mip_height = std::max(mip_height / 2u, 1u);
}
if (total_size != nullptr) {
total_size->size = AlignUp(offset, 256u);
total_size->align = 256;
}
return;
}
if (const uint32_t bytes_per_element = Prospero::NumBytesPerElement(format);
bytes_per_element != 0 && tile == 27 && levels == 1) {
uint32_t block_width = 0;
uint32_t block_height = 0;
EXIT_NOT_IMPLEMENTED(!Gen5Thin64KBBlockSizeFromElementBytes(
bytes_per_element, &block_width, &block_height));
const uint32_t padded_width = AlignUp(pitch, block_width);
const uint32_t padded_height = AlignUp(height, block_height);
const uint32_t size = padded_width * padded_height * bytes_per_element;
if (total_size != nullptr) {
total_size->size = size;
total_size->align = 65536;
}
if (level_sizes != nullptr) {
level_sizes[0].size = size;
level_sizes[0].offset = 0;
}
if (padded_size != nullptr) {
padded_size[0].width = padded_width;
padded_size[0].height = padded_height;
}
return;
}
if (const uint32_t bytes_per_element = Prospero::NumBytesPerElement(format);
bytes_per_element != 0 && tile == 27 && levels > 1) {
uint32_t block_width = 0;
uint32_t block_height = 0;
EXIT_NOT_IMPLEMENTED(!Gen5Thin64KBBlockSizeFromElementBytes(
bytes_per_element, &block_width, &block_height));
const uint32_t bytes_log2 = IntLog2(bytes_per_element);
const uint32_t tail_width_limit = block_width >> 1u;
const uint32_t tail_height_limit = block_height;
constexpr uint32_t max_tail_levels = 12u;
uint32_t first_tail_level = levels;
for (uint32_t l = 0; l < levels; l++) {
const uint32_t mip_pitch = std::max(ShiftCeil(pitch, l), 1u);
const uint32_t mip_height = std::max(ShiftCeil(height, l), 1u);
if (mip_pitch <= tail_width_limit && mip_height <= tail_height_limit &&
levels - l <= max_tail_levels) {
first_tail_level = l;
break;
}
}
uint32_t offset = (first_tail_level < levels ? 65536u : 0u);
for (int32_t l = static_cast<int32_t>(first_tail_level) - 1; l >= 0; l--) {
const uint32_t level = static_cast<uint32_t>(l);
const uint32_t mip_pitch = std::max(ShiftCeil(pitch, level), 1u);
const uint32_t mip_height = std::max(ShiftCeil(height, level), 1u);
const uint32_t padded_width = AlignUp(mip_pitch, block_width);
const uint32_t padded_height = AlignUp(mip_height, block_height);
const uint32_t size = padded_width * padded_height * bytes_per_element;
if (level_sizes != nullptr) {
level_sizes[level].size = size;
level_sizes[level].offset = offset;
level_sizes[level].src_size = size;
level_sizes[level].src_offset = offset;
level_sizes[level].x = 0;
level_sizes[level].y = 0;
}
if (padded_size != nullptr) {
padded_size[level].width = padded_width;
padded_size[level].height = padded_height;
}
offset += size;
}
uint32_t tail_linear_offset = 0;
for (uint32_t l = first_tail_level; l < levels; l++) {
const uint32_t mip_pitch = std::max(ShiftCeil(pitch, l), 1u);
const uint32_t mip_height = std::max(ShiftCeil(height, l), 1u);
const uint32_t linear_size = mip_pitch * mip_height * bytes_per_element;
const auto& tail_location =
GEN5_MIP_TAIL_LOCATIONS_THIN_64KB[bytes_log2][l - first_tail_level];
if (level_sizes != nullptr) {
level_sizes[l].size = linear_size;
level_sizes[l].offset = tail_linear_offset;
level_sizes[l].src_size = 65536u;
level_sizes[l].src_offset = 0;
level_sizes[l].x = tail_location.x;
level_sizes[l].y = tail_location.y;
}
if (padded_size != nullptr) {
padded_size[l].width = block_width;
padded_size[l].height = block_height;
}
tail_linear_offset += AlignUp(linear_size, bytes_per_element);
}
EXIT_NOT_IMPLEMENTED(first_tail_level < levels && tail_linear_offset > 65536u);
if (total_size != nullptr) {
total_size->size = AlignUp(offset, 65536u);
total_size->align = 65536;
}
return;
}
if (auto bytes_per_block = Prospero::BlockCompressedBytesPerBlock(format);
bytes_per_block != 0 && tile == 0) {
uint32_t mip_pitch[16] {};
uint32_t mip_height[16] {};
uint32_t mip_size[16] {};
const uint32_t blocks_w0 = std::max((width + 3u) / 4u, 1u);
const uint32_t blocks_h0 = std::max((height + 3u) / 4u, 1u);
for (uint32_t l = 0; l < levels; l++) {
uint32_t padded_blocks_h = 0;
const uint32_t aligned_blocks_w = CalcLinearAlignedLevelPitch(
blocks_w0, blocks_h0, l, bytes_per_block, &padded_blocks_h, &mip_size[l]);
mip_pitch[l] = std::max(aligned_blocks_w * 4u, 32u);
mip_height[l] = std::max(padded_blocks_h * 4u, 32u);
}
const uint32_t total = SetLinearMipChainLayout(levels, mip_pitch, mip_height, mip_size,
level_sizes, padded_size);
if (total_size != nullptr) {
total_size->size = total;
total_size->align = 256;
}
return;
}
uint32_t bytes_per_element = 0;
uint32_t texels_per_element_wide = 0;
uint32_t texels_per_element_tall = 0;
uint32_t block_width_log2 = 0;
uint32_t block_height_log2 = 0;
if (tile == 5 && levels == 1 &&
Gen5Standard4KBLayout(format, &bytes_per_element, &texels_per_element_wide,
&texels_per_element_tall, &block_width_log2, &block_height_log2)) {
const uint32_t row_elements =
std::max((pitch + texels_per_element_wide - 1u) / texels_per_element_wide, 1u);
const uint32_t height_elements =
std::max((height + texels_per_element_tall - 1u) / texels_per_element_tall, 1u);
const uint32_t block_width = 1u << block_width_log2;
const uint32_t block_height = 1u << block_height_log2;
const uint32_t padded_width = (row_elements + block_width - 1u) & ~(block_width - 1u);
const uint32_t padded_height = (height_elements + block_height - 1u) & ~(block_height - 1u);
const uint32_t size = padded_width * padded_height * bytes_per_element;
if (total_size != nullptr) {
total_size->size = size;
total_size->align = 4096;
}
if (level_sizes != nullptr) {
level_sizes[0].size = size;
level_sizes[0].offset = 0;
}
if (padded_size != nullptr) {
padded_size[0].width = padded_width * texels_per_element_wide;
padded_size[0].height = padded_height * texels_per_element_tall;
}
return;
}
if (tile == 5 && levels > 1 &&
Gen5Standard4KBLayout(format, &bytes_per_element, &texels_per_element_wide,
&texels_per_element_tall, &block_width_log2, &block_height_log2)) {
const uint32_t row_elements0 =
std::max((pitch + texels_per_element_wide - 1u) / texels_per_element_wide, 1u);
const uint32_t height_elements0 =
std::max((height + texels_per_element_tall - 1u) / texels_per_element_tall, 1u);
const uint32_t block_width = 1u << block_width_log2;
const uint32_t block_height = 1u << block_height_log2;
const uint32_t bytes_log2 = IntLog2(bytes_per_element);
const uint32_t tail_width_limit = block_width >> 1u;
const uint32_t tail_height_limit = block_height;
uint32_t first_tail_level = levels;
for (uint32_t l = 0; l < levels; l++) {
const uint32_t row_elements = std::max(ShiftCeil(row_elements0, l), 1u);
const uint32_t height_elements = std::max(ShiftCeil(height_elements0, l), 1u);
if (row_elements <= tail_width_limit && height_elements <= tail_height_limit &&
levels - l <= 8) {
first_tail_level = l;
break;
}
}
uint32_t offset = (first_tail_level < levels ? 4096u : 0u);
for (int32_t l = static_cast<int32_t>(first_tail_level) - 1; l >= 0; l--) {
const uint32_t level = static_cast<uint32_t>(l);
const uint32_t row_elements = std::max(ShiftCeil(row_elements0, level), 1u);
const uint32_t height_elements = std::max(ShiftCeil(height_elements0, level), 1u);
const uint32_t padded_width = AlignUp(row_elements, block_width);
const uint32_t padded_height = AlignUp(height_elements, block_height);
const uint32_t size = padded_width * padded_height * bytes_per_element;
if (level_sizes != nullptr) {
level_sizes[level].size = size;
level_sizes[level].offset = offset;
level_sizes[level].src_size = size;
level_sizes[level].src_offset = offset;
level_sizes[level].x = 0;
level_sizes[level].y = 0;
}
if (padded_size != nullptr) {
padded_size[level].width = padded_width * texels_per_element_wide;
padded_size[level].height = padded_height * texels_per_element_tall;
}
offset += size;
}
uint32_t tail_linear_offset = 0;
for (uint32_t l = first_tail_level; l < levels; l++) {
const uint32_t row_elements = std::max(ShiftCeil(row_elements0, l), 1u);
const uint32_t height_elements = std::max(ShiftCeil(height_elements0, l), 1u);
const uint32_t linear_size = row_elements * height_elements * bytes_per_element;
const auto& tail_location =
GEN5_MIP_TAIL_LOCATIONS_THIN_4KB[bytes_log2][l - first_tail_level];
if (level_sizes != nullptr) {
level_sizes[l].size = linear_size;
level_sizes[l].offset = tail_linear_offset;
level_sizes[l].src_size = 4096u;
level_sizes[l].src_offset = 0;
level_sizes[l].x = tail_location.x;
level_sizes[l].y = tail_location.y;
}
if (padded_size != nullptr) {
padded_size[l].width = block_width * texels_per_element_wide;
padded_size[l].height = block_height * texels_per_element_tall;
}
tail_linear_offset += AlignUp(linear_size, bytes_per_element);
}
EXIT_NOT_IMPLEMENTED(first_tail_level < levels && tail_linear_offset > 4096u);
if (total_size != nullptr) {
total_size->size = AlignUp(offset, 4096u);
total_size->align = 4096;
}
return;
}
if (tile == 24 && levels == 1) {
const uint32_t bytes_per_element = Prospero::NumBytesPerElement(format);
if (bytes_per_element != 0) {
uint32_t block_width = 0;
uint32_t block_height = 0;
const bool supported = bytes_per_element <= 4 &&
Gen5Thin64KBBlockSizeFromElementBytes(
bytes_per_element, &block_width, &block_height);
if (supported) {
const uint32_t padded_width = (pitch + block_width - 1u) & ~(block_width - 1u);
const uint32_t padded_height = AlignUp(height, block_height);
const uint32_t size = padded_width * padded_height * bytes_per_element;
if (total_size != nullptr) {
total_size->size = size;
total_size->align = 65536;
}
if (level_sizes != nullptr) {
level_sizes[0].size = size;
level_sizes[0].offset = 0;
}
if (padded_size != nullptr) {
padded_size[0].width = padded_width;
padded_size[0].height = padded_height;
}
return;
}
}
}
if (tile == 24 && levels > 1) {
const uint32_t bytes_per_element = Prospero::NumBytesPerElement(format);
if (bytes_per_element != 0) {
uint32_t block_width = 0;
uint32_t block_height = 0;
const bool supported = bytes_per_element <= 4 &&
Gen5Thin64KBBlockSizeFromElementBytes(
bytes_per_element, &block_width, &block_height);
if (supported) {
static bool logged = false;
if (!logged) {
LOGF("\t temporary: sizing PS5 depth tiled texture with mips, format = %u, "
"levels = %u\n",
format, levels);
logged = true;
}
uint32_t offset = 0;
uint32_t mip_pitch = pitch;
uint32_t mip_height = height;
for (uint32_t l = 0; l < levels; l++) {
offset = AlignUp(offset, 65536u);
const uint32_t padded_width = AlignUp(mip_pitch, block_width);
const uint32_t padded_height = AlignUp(mip_height, block_height);
const uint32_t size = padded_width * padded_height * bytes_per_element;
if (level_sizes != nullptr) {
level_sizes[l].size = size;
level_sizes[l].offset = offset;
}
if (padded_size != nullptr) {
padded_size[l].width = padded_width;
padded_size[l].height = padded_height;
}
offset += size;
mip_pitch = std::max(mip_pitch / 2u, 1u);
mip_height = std::max(mip_height / 2u, 1u);
}
if (total_size != nullptr) {
total_size->size = AlignUp(offset, 65536u);
total_size->align = 65536;
}
return;
}
}
}
uint32_t std64_bytes_per_element = 0;
uint32_t std64_texels_per_element_wide = 0;
uint32_t std64_texels_per_element_tall = 0;
uint32_t std64_block_width_log2 = 0;
uint32_t std64_block_height_log2 = 0;
if (tile == 9 && levels > 1 &&
Gen5Standard64KBLayout(format, &std64_bytes_per_element, &std64_texels_per_element_wide,
&std64_texels_per_element_tall, &std64_block_width_log2,
&std64_block_height_log2)) {
const uint32_t row_elements0 = std::max(
(pitch + std64_texels_per_element_wide - 1u) / std64_texels_per_element_wide, 1u);
const uint32_t height_elements0 = std::max(
(height + std64_texels_per_element_tall - 1u) / std64_texels_per_element_tall, 1u);
const uint32_t block_width = 1u << std64_block_width_log2;
const uint32_t block_height = 1u << std64_block_height_log2;
const uint32_t bytes_log2 = IntLog2(std64_bytes_per_element);
const uint32_t tail_width_limit = block_width >> 1u;
const uint32_t tail_height_limit = block_height;
constexpr uint32_t max_tail_levels = 12u;
uint32_t first_tail_level = levels;
for (uint32_t l = 0; l < levels; l++) {
const uint32_t row_elements = std::max(ShiftCeil(row_elements0, l), 1u);
const uint32_t height_elements = std::max(ShiftCeil(height_elements0, l), 1u);
if (row_elements <= tail_width_limit && height_elements <= tail_height_limit &&
levels - l <= max_tail_levels) {
first_tail_level = l;
break;
}
}
uint32_t offset = (first_tail_level < levels ? 65536u : 0u);
for (int32_t l = static_cast<int32_t>(first_tail_level) - 1; l >= 0; l--) {
const uint32_t level = static_cast<uint32_t>(l);
const uint32_t row_elements = std::max(ShiftCeil(row_elements0, level), 1u);
const uint32_t height_elements = std::max(ShiftCeil(height_elements0, level), 1u);
const uint32_t padded_width = AlignUp(row_elements, block_width);
const uint32_t padded_height = AlignUp(height_elements, block_height);
const uint32_t size = padded_width * padded_height * std64_bytes_per_element;
if (level_sizes != nullptr) {
level_sizes[level].size = size;
level_sizes[level].offset = offset;
level_sizes[level].src_size = size;
level_sizes[level].src_offset = offset;
level_sizes[level].x = 0;
level_sizes[level].y = 0;
}
if (padded_size != nullptr) {
padded_size[level].width = padded_width * std64_texels_per_element_wide;
padded_size[level].height = padded_height * std64_texels_per_element_tall;
}
offset += size;
}
uint32_t tail_linear_offset = 0;
for (uint32_t l = first_tail_level; l < levels; l++) {
const uint32_t row_elements = std::max(ShiftCeil(row_elements0, l), 1u);
const uint32_t height_elements = std::max(ShiftCeil(height_elements0, l), 1u);
const uint32_t linear_size = row_elements * height_elements * std64_bytes_per_element;
const auto& tail_location =
GEN5_MIP_TAIL_LOCATIONS_THIN_64KB[bytes_log2][l - first_tail_level];
if (level_sizes != nullptr) {
level_sizes[l].size = linear_size;
level_sizes[l].offset = tail_linear_offset;
level_sizes[l].src_size = 65536u;
level_sizes[l].src_offset = 0;
level_sizes[l].x = tail_location.x;
level_sizes[l].y = tail_location.y;
}
if (padded_size != nullptr) {
padded_size[l].width = block_width * std64_texels_per_element_wide;
padded_size[l].height = block_height * std64_texels_per_element_tall;
}
tail_linear_offset += AlignUp(linear_size, std64_bytes_per_element);
}
EXIT_NOT_IMPLEMENTED(first_tail_level < levels && tail_linear_offset > 65536u);
if (total_size != nullptr) {
total_size->size = AlignUp(offset, 65536u);
total_size->align = 65536;
}
return;
}
if (tile == 9 && levels == 1 &&
Gen5Standard64KBLayout(format, &std64_bytes_per_element, &std64_texels_per_element_wide,
&std64_texels_per_element_tall, &std64_block_width_log2,
&std64_block_height_log2)) {
const uint32_t row_elements = std::max(
(pitch + std64_texels_per_element_wide - 1u) / std64_texels_per_element_wide, 1u);
const uint32_t height_elements = std::max(
(height + std64_texels_per_element_tall - 1u) / std64_texels_per_element_tall, 1u);
const uint32_t block_width = 1u << std64_block_width_log2;
const uint32_t block_height = 1u << std64_block_height_log2;
const uint32_t padded_width = AlignUp(row_elements, block_width);
const uint32_t padded_height = AlignUp(height_elements, block_height);
const uint32_t size = padded_width * padded_height * std64_bytes_per_element;
if (total_size != nullptr) {
total_size->size = size;
total_size->align = 65536;
}
if (level_sizes != nullptr) {
level_sizes[0].size = size;
level_sizes[0].offset = 0;
}
if (padded_size != nullptr) {
padded_size[0].width = padded_width * std64_texels_per_element_wide;
padded_size[0].height = padded_height * std64_texels_per_element_tall;
}
return;
}
if (total_size != nullptr && total_size->size == 0) {
std::vector<std::string> list;
list.push_back(fmt::format("format = {}", format));
list.push_back(fmt::format("width = {}", width));
list.push_back(fmt::format("height = {}", height));
list.push_back(fmt::format("pitch = {}", pitch));
list.push_back(fmt::format("levels = {}", levels));
list.push_back(fmt::format("tile = {}", tile));
EXIT("unknown format:\n%s\n", Common::Concat(list, '\n').c_str());
}
}
void TileGetTextureTotalSize(uint32_t format, uint32_t width, uint32_t height, uint32_t depth,
uint32_t pitch, uint32_t levels, uint32_t tile, bool volume_texture,
TileSizeAlign* total_size) {
EXIT_NOT_IMPLEMENTED(depth == 0);
TileSizeAlign slice_size {};
TileGetTextureSize(format, width, height, pitch, levels, tile, &slice_size, nullptr, nullptr);
*total_size = slice_size;
uint64_t total = static_cast<uint64_t>(slice_size.size) * depth;
uint32_t bytes_per_element = 0;
uint32_t texels_per_element_wide = 0;
uint32_t texels_per_element_tall = 0;
uint32_t block_width_log2 = 0;
uint32_t block_height_log2 = 0;
uint32_t block_depth_log2 = 0;
if (volume_texture && depth > 1 && tile == 5 &&
TileGetStandard4KBVolumeLayout(format, &bytes_per_element, &texels_per_element_wide,
&texels_per_element_tall, &block_width_log2,
&block_height_log2, &block_depth_log2)) {
const uint32_t row_elements0 =
std::max((pitch + texels_per_element_wide - 1u) / texels_per_element_wide, 1u);
const uint32_t height_elements0 =
std::max((height + texels_per_element_tall - 1u) / texels_per_element_tall, 1u);
const uint32_t block_width = 1u << block_width_log2;
const uint32_t block_height = 1u << block_height_log2;
const uint32_t block_depth = 1u << block_depth_log2;
uint64_t block_slice_size = 0;
if (levels > 1) {
const uint32_t tail_width_limit = block_width;
const uint32_t tail_height_limit = block_height >> 1u;
constexpr uint32_t max_tail_levels = 5u;
for (uint32_t l = 0; l < levels; l++) {
const uint32_t row_elements = std::max(ShiftCeil(row_elements0, l), 1u);
const uint32_t height_elements = std::max(ShiftCeil(height_elements0, l), 1u);
if (row_elements <= tail_width_limit && height_elements <= tail_height_limit &&
levels - l <= max_tail_levels) {
block_slice_size += 4096u;
break;
}
block_slice_size += static_cast<uint64_t>(block_depth) *
AlignUp(row_elements, block_width) *
AlignUp(height_elements, block_height) * bytes_per_element;
}
} else {
block_slice_size = static_cast<uint64_t>(block_depth) *
AlignUp(row_elements0, block_width) *
AlignUp(height_elements0, block_height) * bytes_per_element;
}
total = block_slice_size * ShiftCeil(depth, block_depth_log2);
total_size->align = 4096;
}
EXIT_NOT_IMPLEMENTED(total > 0xffffffffull);
total_size->size = static_cast<uint32_t>(total);
}
uint32_t TileGetTexturePitch(uint32_t format, uint32_t width, uint32_t levels, uint32_t tile) {
uint32_t pitch = width;
if (tile == 27) {
if (const uint32_t bytes_per_element = Prospero::NumBytesPerElement(format); bytes_per_element != 0) {
uint32_t block_width = 0;
uint32_t block_height = 0;
EXIT_NOT_IMPLEMENTED(!Gen5Thin64KBBlockSizeFromElementBytes(
bytes_per_element, &block_width, &block_height));
pitch = AlignUp(pitch, block_width);
}
}
if (tile == 0) {
if (const auto bytes_per_element = Prospero::NumBytesPerElement(format); bytes_per_element != 0) {
pitch = AlignUp(pitch * bytes_per_element, 256u) / bytes_per_element;
}
}
uint32_t bytes_per_element = 0;
uint32_t texels_per_element_wide = 0;
uint32_t texels_per_element_tall = 0;
uint32_t block_width_log2 = 0;
uint32_t block_height_log2 = 0;
if (tile == 9 &&
Gen5Standard64KBLayout(format, &bytes_per_element, &texels_per_element_wide,
&texels_per_element_tall, &block_width_log2, &block_height_log2)) {
pitch = AlignUp(pitch, (1u << block_width_log2) * texels_per_element_wide);
}
if (tile == 24) {
const uint32_t bytes_per_element = Prospero::NumBytesPerElement(format);
uint32_t block_width = 0;
uint32_t block_height = 0;
if (bytes_per_element <= 4 && Gen5Thin64KBBlockSizeFromElementBytes(
bytes_per_element, &block_width, &block_height)) {
pitch = AlignUp(pitch, block_width);
}
}
return pitch;
}
} // namespace Libs::Graphics