mirror of
https://github.com/valentderah/totk-pc-recomp.git
synced 2026-11-11 01:41:02 +00:00
Within a row, consecutive GOB columns are one block apart and a GOB row's four 16-byte segments sit at fixed offsets, so the address is computed once per row. Non-ASTC texture upload time in the monsters2 load frames falls by 28-38% (90 to 65 ms at the world load). probe-vkmem compares it with its reference swizzle. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
131 lines
7.4 KiB
C
131 lines
7.4 KiB
C
// Tegra X1 block-linear <-> linear copies.
|
|
// Ported from MacNativeGames games/totk/third_party/d2r_gfx/gfx/tex/nvn_deswizzle.c @ b75be8cf (MIT, Copyright (c) 2026 jhaizhou); comments translated, the copy loop split out as tex_bl_copy.
|
|
//
|
|
// Layout (public descriptions: deko3d, Ryujinx BlockLinearLayout):
|
|
// GOB = 64 bytes x 8 rows = 512 bytes; offset of (x byte, y row) within a GOB =
|
|
// (x%64/32)*256 + (y%8/2)*64 + (x%32/16)*32 + (y%2)*16 + x%16
|
|
// block = 1 GOB wide x 2^th GOBs high x 2^td GOBs deep, ordered y first then z inside a block; blocks fill the level
|
|
// in x -> y -> z row-major order.
|
|
// Level and layer start offsets and the block height and depth of a level come from nvn_tex.c; this file only does the
|
|
// addressing inside a level and the copy.
|
|
#include "tex.h"
|
|
#include <string.h>
|
|
|
|
static uint32_t gob_off(uint32_t x, uint32_t y) {
|
|
return (x % 64 / 32) * 256 + (y % 8 / 2) * 64 + (x % 32 / 16) * 32 + (y % 2) * 16 + x % 16;
|
|
}
|
|
|
|
uint64_t nvn_bl_offset(uint32_t x, uint32_t y, uint32_t z, uint32_t row_bytes, uint32_t rows, unsigned th, unsigned td) {
|
|
uint32_t bh = 1u << th, bd = 1u << td;
|
|
uint64_t gobs_x = (row_bytes + 63) / 64, blocks_y = (rows + 8 * bh - 1) / (8 * bh);
|
|
uint64_t block = 512ull * bh * bd; // bytes in one block
|
|
return (z / bd) * gobs_x * blocks_y * block // which block layer (along depth)
|
|
+ (y / (8 * bh)) * gobs_x * block // which block row
|
|
+ (x / 64) * block // which block column
|
|
+ ((z % bd) * bh + (y % (8 * bh)) / 8) * 512ull // which GOB within the block
|
|
+ gob_off(x, y);
|
|
}
|
|
|
|
static uint32_t mip(uint32_t s, unsigned l) { s >>= l; return s ? s : 1; }
|
|
|
|
// Geometry of one level: bytes per row of blocks, block rows, slices, where the level and layer start in the storage,
|
|
// and the level's block height and depth. -1 for textures the copy does not handle.
|
|
int nvn_deswizzle_geom(const nvn_texdesc *d, unsigned level, unsigned layer,
|
|
uint32_t *row_bytes, uint32_t *rows, uint32_t *slices, uint64_t *base, unsigned *th, unsigned *td, uint32_t *bpb) {
|
|
if (d->target == 5 || d->target == 6 || d->target == 10 || nvn_tex_is_linear(d) || d->samples > 1) return -1; // MS / BUFFER / LINEAR
|
|
int bw, bh; *bpb = (uint32_t)nvn_fmt_bpb(d->format, &bw, &bh);
|
|
if (!*bpb || level >= (d->levels ? d->levels : 1)) return -1;
|
|
nvn_texlayout L; nvn_tex_layout(d, &L);
|
|
if (layer >= L.layers) return -1;
|
|
int one_d = d->target == 0 || d->target == 3;
|
|
uint32_t w = mip(d->width, level), h = one_d ? 1 : mip(d->height, level);
|
|
*slices = d->target == 2 ? mip(d->depth ? d->depth : 1, level) : 1;
|
|
*row_bytes = (w + bw - 1) / bw * *bpb; *rows = (h + bh - 1) / bh;
|
|
*base = (uint64_t)layer * L.layer_size + nvn_tex_level_offset(d, level);
|
|
nvn_tex_level_tiles(d, level, th, td);
|
|
return 0;
|
|
}
|
|
|
|
// nvn_bl_offset per row instead of per 16 bytes: within a row, GOB column k starts k blocks further, and the four
|
|
// 16-byte segments of a GOB row (x % 64 = 0, 16, 32, 48) sit at 0, 32, 256 and 288 of it.
|
|
void tex_bl_copy(uint8_t *bl, uint8_t *lin, size_t pitch, uint32_t row_bytes, uint32_t rows, uint32_t slices,
|
|
unsigned th, unsigned td, int to_linear) {
|
|
static const uint32_t seg[4] = {0, 32, 256, 288};
|
|
uint64_t block = 512ull << th << td;
|
|
for (uint32_t z = 0; z < slices; z++)
|
|
for (uint32_t y = 0; y < rows; y++) {
|
|
uint8_t *row = lin + ((size_t)z * rows + y) * pitch, *g = bl + nvn_bl_offset(0, y, z, row_bytes, rows, th, td);
|
|
uint32_t x = 0;
|
|
if (to_linear)
|
|
for (; x + 64 <= row_bytes; x += 64, g += block)
|
|
for (int s = 0; s < 4; s++) memcpy(row + x + 16 * s, g + seg[s], 16);
|
|
else
|
|
for (; x + 64 <= row_bytes; x += 64, g += block)
|
|
for (int s = 0; s < 4; s++) memcpy(g + seg[s], row + x + 16 * s, 16);
|
|
for (int s = 0; x < row_bytes; s++, x += 16) { // a partial GOB at the row's end
|
|
uint32_t n = row_bytes - x < 16 ? row_bytes - x : 16;
|
|
if (to_linear) memcpy(row + x, g + seg[s], n); else memcpy(g + seg[s], row + x, n);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int move(const nvn_texdesc *d, unsigned level, unsigned layer, uint8_t *bl, uint8_t *lin, size_t pitch, int to_linear) {
|
|
if (nvn_tex_is_linear(d)) {
|
|
if (level || layer || d->samples > 1 || (d->target != 0 && d->target != 1 && d->target != 7)) return -1;
|
|
int bw, bh, bpb = nvn_fmt_bpb(d->format, &bw, &bh);
|
|
if (!bpb) return -1;
|
|
size_t row = (size_t)((d->width + bw - 1) / bw) * bpb;
|
|
uint32_t rows = d->target == 0 ? 1 : (d->height + bh - 1) / bh, stride = nvn_tex_linear_stride(d);
|
|
if (pitch < row || stride < row) return -1;
|
|
for (uint32_t y = 0; y < rows; y++) {
|
|
if (to_linear) memcpy(lin + y * pitch, bl + (size_t)y * stride, row);
|
|
else memcpy(bl + (size_t)y * stride, lin + y * pitch, row);
|
|
}
|
|
return 0;
|
|
}
|
|
uint32_t row_bytes, rows, slices, bpb; uint64_t base; unsigned th, td;
|
|
if (nvn_deswizzle_geom(d, level, layer, &row_bytes, &rows, &slices, &base, &th, &td, &bpb)) return -1;
|
|
if (pitch < row_bytes) return -1;
|
|
tex_bl_copy(bl + base, lin, pitch, row_bytes, rows, slices, th, td, to_linear);
|
|
return 0;
|
|
}
|
|
|
|
int nvn_texels_box(const nvn_texdesc *d, unsigned level, unsigned layer, uint32_t x, uint32_t y, uint32_t z, uint32_t wb,
|
|
uint32_t h, void *storage, void *rows, size_t pitch, int to_linear) {
|
|
uint8_t *st = storage, *ln = rows;
|
|
if (nvn_tex_is_linear(d)) {
|
|
if (level || layer || z || d->samples > 1 || (d->target != 0 && d->target != 1 && d->target != 7)) return -1;
|
|
int bw, bh, bpb = nvn_fmt_bpb(d->format, &bw, &bh);
|
|
if (!bpb) return -1;
|
|
size_t row = (size_t)((d->width + bw - 1) / bw) * bpb;
|
|
uint32_t nrows = d->target == 0 ? 1 : (d->height + bh - 1) / bh, stride = nvn_tex_linear_stride(d);
|
|
if (stride < row || x + (size_t)wb > row || y + (uint64_t)h > nrows) return -1;
|
|
for (uint32_t r = 0; r < h; r++) {
|
|
uint8_t *p = st + (size_t)(y + r) * stride + x, *q = ln + r * pitch;
|
|
if (to_linear) memcpy(q, p, wb); else memcpy(p, q, wb);
|
|
}
|
|
return 0;
|
|
}
|
|
uint32_t row_bytes, nrows, slices, bpb; uint64_t base; unsigned th, td;
|
|
if (nvn_deswizzle_geom(d, level, layer, &row_bytes, &nrows, &slices, &base, &th, &td, &bpb)) return -1;
|
|
if (x + (uint64_t)wb > row_bytes || y + (uint64_t)h > nrows || z >= slices) return -1;
|
|
for (uint32_t r = 0; r < h; r++) {
|
|
uint8_t *q = ln + r * pitch;
|
|
for (uint32_t i = 0; i < wb;) {
|
|
uint32_t xb = x + i, n = 16 - xb % 16; // contiguous up to the end of its 16-byte segment
|
|
if (n > wb - i) n = wb - i;
|
|
uint8_t *p = st + base + nvn_bl_offset(xb, y + r, z, row_bytes, nrows, th, td);
|
|
if (to_linear) memcpy(q + i, p, n); else memcpy(p, q + i, n);
|
|
i += n;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int nvn_deswizzle(const nvn_texdesc *d, unsigned level, unsigned layer, const void *src, void *dst, size_t dst_pitch) {
|
|
return move(d, level, layer, (uint8_t *)src, (uint8_t *)dst, dst_pitch, 1);
|
|
}
|
|
int nvn_swizzle(const nvn_texdesc *d, unsigned level, unsigned layer, const void *src, size_t src_pitch, void *dst) {
|
|
return move(d, level, layer, (uint8_t *)dst, (uint8_t *)src, src_pitch, 0);
|
|
}
|