nCine::RHI::PICA::PicaTexture class

Texture object of the PICA backend (aliased as RHI::Texture).

Keeps a LINEAR host store of level 0 in the uploaded pixel format (the same native R8/RG8/RGBA8 layout the software and PVR backends use) plus a second store the PICA200 can sample directly. That store lives in the linear heap - the part of main memory the GPU addresses physically, which libctru hands out through linearAlloc() - rather than in the 6 MB of VRAM, where only the framebuffers and the render targets are worth their space. It has to satisfy four hardware requirements the host store does not:

  • Power-of-two dimensions between 8 and 1024 per axis. Anything larger is split into pages of at most 1024x1024, and the draw path selects the page a primitive's texture rectangle falls into and rebases its texture coordinates onto it (see AcquirePage()). Every sprite/tile draw samples one small sub-rect of an atlas, so a primitive practically never straddles a page boundary; the game's own content is assembled against the 512 the chunking asks for anyway.
  • A format the PICA200 samples. The GPU has no colour lookup table of any kind - its 8-bit formats are luminance and alpha - so BOTH indexed formats are baked through their palette row into colour on the CPU (EnsureBakedStore()): R8 with the palette entry's alpha, RG8 (index + per-pixel alpha) with the texel's own. The bake goes into GPU_RGBA4, and so does true-colour RGB8/RGBA8 content, which halves the store and the sampling bandwidth over RGBA8 for a 2D game whose colours are palette entries to begin with; RGB565 stays GPU_RGB565, whose channel order matches the engine's.
  • The 8x8 tiled layout. The GPU reads every texture as consecutive 8x8 blocks in Morton (Z) order - there is no linear texture mode - so every store is tiled as it is built (see TilePage()).
  • The CPU cache written back, since the GPU reads the linear heap without seeing the ARM11's data cache. Every store rebuild ends in a GSPGPU_FlushDataCache() of exactly the bytes it wrote.

A render target's store is a colour buffer the GPU renders into (in VRAM when it fits, in the linear heap otherwise) wrapped in a C3D_RenderTarget; the framebuffer layout IS the tiled texture layout, so it is sampled afterwards without any conversion.

Mip levels above 0 and compressed formats are accepted but not stored, exactly like on the other fixed-function backends: the game never uses either.

Public types

struct Page
One GPU-addressable piece of the texture.

Public static variables

static std::uint32_t MaxTextureUnits constexpr
Number of texture units tracked by the device.
static std::int32_t MaxPageDimension constexpr
The dimension a single PICA200 texture cannot exceed, per axis (a hardware limit).
static std::int32_t MinPageDimension constexpr
The dimension a single PICA200 texture cannot fall short of, per axis (a hardware limit).

Public static functions

static auto Unbind(std::uint32_t textureUnit) -> bool
Unbinds any texture from the specified texture unit.
static void SetUnpackAlignment(std::int32_t alignment)
Sets the client pixel-row alignment of uploads (ignored, uploads are tightly packed).
static auto SupportsImmutableStorage() -> bool
static auto SupportsTextureReadback() -> bool
static void ClearErrors()
static auto CheckErrors() -> bool
static void CheckFormatSupport(PixelFormat format)
static auto BytesPerPixel(PixelFormat format) -> std::int32_t
Returns the number of bytes occupied by one texel of the given format (0 if unsupported).
static void TileBand16(std::uint16_t* dst, const std::uint16_t* band, std::int32_t paddedWidth)
Scatters eight linear rows of 16-bit texels into the tile row of a GPU store they belong to.

Constructors, destructors, conversion operators

PicaTexture(TextureTarget target) explicit
~PicaTexture()
PicaTexture(const PicaTexture&) deleted

Public functions

auto operator=(const PicaTexture&) -> PicaTexture& deleted
auto GetUniqueId() const -> std::uint32_t
Returns a backend-neutral identifier uniquely identifying the texture (feeds material sort keys).
auto GetTarget() const -> TextureTarget
Returns the texture target.
auto GetWidth() const -> std::int32_t
Returns the width of level 0 in texels.
auto GetHeight() const -> std::int32_t
Returns the height of level 0 in texels.
auto GetFormat() const -> PixelFormat
Returns the pixel format of the linear host store (native, like the software backend).
auto GetUploadFormat() const -> PixelFormat
Returns the original upload format (R8/RG8 kept so the palette path can tell them apart).
auto GetStrideBytes() const -> std::int32_t
Returns the byte distance between two consecutive rows of the linear host store.
auto GetPixels(std::int32_t level = 0) const -> const std::uint8_t*
Returns the base pointer of the linear host store (may be nullptr before an upload).
auto MutablePixels() -> std::uint8_t*
Returns a writable base pointer of the linear host store (nullptr before an upload).
auto GetWrapS() const -> SamplerWrapping
Returns the horizontal texture-coordinate wrap mode.
auto GetWrapT() const -> SamplerWrapping
Returns the vertical texture-coordinate wrap mode (single stored mode).
auto GetSwizzle() const -> const SwizzleChannel*
Returns the four-channel sampling swizzle (identity by default; informational here).
auto GetMagFiltering() const -> nCine::SamplerFilter
Returns the magnification filter.
auto GetMagFilter() const -> nCine::SamplerFilter
Alias of GetMagFiltering().
auto IsRenderTarget() const -> bool
Returns true if the texture is bound as a color render target.
void SetRenderTarget(bool isRenderTarget)
Marks the texture as (or no longer as) a color render target; becoming one allocates its colour buffer.
auto GetContentVersion() const -> std::uint32_t
Returns a globally monotonic stamp of the texel store, advanced by every allocation or upload.
auto IsIndexed() const -> bool
Returns true when the store holds palette indices (R8) - the GPU cannot sample those, see NeedsPaletteBake().
auto NeedsPaletteBake() const -> bool
Returns true when the texture needs the per-palette-row CPU bake (both indexed formats, there is no palette hardware).
auto IsPaletteTexture() const -> bool
Returns true when this is the intercepted shared palette texture (read by the bakes, never sampled).
auto AcquirePage(std::int32_t texelX, std::int32_t texelY) -> const Page*
Builds the GPU store if it is missing and returns the page holding the given source texel.
auto GetPicaFormat() const -> GPU_TEXCOLOR
Returns the GPU_TEXCOLOR format of the GPU store (valid once a page exists).
auto GetPageCountX() const -> std::int32_t
Number of pages the image is split into along each axis (1 x 1 for anything up to 1024x1024).
auto GetPageCountY() const -> std::int32_t
Number of pages the image is split into along each axis.
auto EnsureBakedStore(const PicaTexture* palette, std::int32_t paletteOffset, std::uint32_t paletteGeneration) -> bool
Makes the GPU store hold this indexed texture baked through one palette row.
void ReleaseHostCopy()
Releases the decoded texels once the GPU store built from them is the only copy needed.
auto MapStreamingTexels(std::int32_t& strideBytes) -> void*
Declined here: always returns nullptr (the contract's streaming-texture fast path).
auto GetRenderTarget() const -> C3D_RenderTarget*
Returns the render target the GPU renders into when this texture is one, or nullptr.
auto Bind(std::uint32_t textureUnit) const -> bool
Binds the texture to the specified texture unit on the device.
auto Bind() const -> bool
Binds the texture to texture unit 0.
auto Unbind() const -> bool
Unbinds the texture from the unit it was last bound to.
void TexImage2D(std::int32_t level, PixelFormat format, bool bgr, std::int32_t width, std::int32_t height, const void* data)
Allocates level-0 storage of the given format/size and optionally uploads its texels.
void TexSubImage2D(std::int32_t level, std::int32_t xoffset, std::int32_t yoffset, std::int32_t width, std::int32_t height, PixelFormat format, bool bgr, const void* data)
Updates a rectangular subregion of level 0.
void TexStorage2D(std::int32_t levels, PixelFormat format, std::int32_t width, std::int32_t height)
Allocates immutable level-0 storage of the given format/size (no texels yet).
void CompressedTexImage2D(std::int32_t level, PixelFormat format, std::int32_t width, std::int32_t height, std::int32_t imageSize, const void* data)
Compressed upload (unsupported, accepted as a no-op).
void CompressedTexSubImage2D(std::int32_t level, std::int32_t xoffset, std::int32_t yoffset, std::int32_t width, std::int32_t height, PixelFormat format, std::int32_t imageSize, const void* data)
Compressed sub-upload (unsupported, accepted as a no-op).
void GetTexImage(std::int32_t level, PixelFormat format, bool bgr, void* pixels)
Reads back level-0 texels of the linear host store into client memory.
void SetMinFiltering(nCine::SamplerFilter filter)
Sets the minification filter.
void SetMagFiltering(nCine::SamplerFilter filter)
Sets the magnification filter.
void SetWrap(SamplerWrapping wrap)
Sets the wrap mode.
void SetSwizzle(SwizzleChannel r, SwizzleChannel g, SwizzleChannel b, SwizzleChannel a)
Sets the sampling swizzle (stored, informational - the palette path keys off the upload format instead).
void SetMaxLevel(std::int32_t maxLevel)
Sets the highest defined mipmap level (ignored).
void SetObjectLabel(StringView label)
Sets a debug label; "Palettes" marks the shared palette texture the bakes read.

Function documentation

static void nCine::RHI::PICA::PicaTexture::TileBand16(std::uint16_t* dst, const std::uint16_t* band, std::int32_t paddedWidth)

Scatters eight linear rows of 16-bit texels into the tile row of a GPU store they belong to.

The GPU reads a texture as 8x8 tiles laid out left to right, tile rows running from the first texel row on: tile (tx, ty) starts at texel index (ty * tilesPerRow + tx) * 64 and holds its 64 texels in Morton (Z) order - the three low bits of x and y interleaved with x in the even positions. dst is the first tile of the band's tile row, band the eight linear rows (paddedWidth texels each). Shared with the device, which tiles the per-frame lightmap texture the same way.

const Page* nCine::RHI::PICA::PicaTexture::AcquirePage(std::int32_t texelX, std::int32_t texelY)

Builds the GPU store if it is missing and returns the page holding the given source texel.

texelX / texelY are clamped into the image, so the min corner of any texture rectangle is a valid argument. Returns nullptr when there is nothing to sample (no upload yet, an unsupported format, or an indexed store whose bake has not been requested - see EnsureBakedStore()).

bool nCine::RHI::PICA::PicaTexture::EnsureBakedStore(const PicaTexture* palette, std::int32_t paletteOffset, std::uint32_t paletteGeneration)

Makes the GPU store hold this indexed texture baked through one palette row.

The row is taken as the 256 RGBA8 entries of palette starting at paletteOffset; the index is resolved through it, and the alpha comes from the texel's own alpha byte (RG8) or from the palette entry (R8), written as GPU_RGBA4 - the analogue of GU::GuTexture::EnsureBakedStore(), applied to both indexed formats here because the PICA200 has no colour lookup table. A small number of bakes is cached, so the common "one extra palette row" case (a sprite and its recolored twin) does not rebuild anything; AcquirePage() then hands out the pages of the matching bake. Returns false when the bake could not be produced.

The offset is taken rather than a ready row pointer so that the 256 entries this reads can be checked to lie inside palette here, once, instead of at each of the callers.

void nCine::RHI::PICA::PicaTexture::ReleaseHostCopy()

Releases the decoded texels once the GPU store built from them is the only copy needed.

Both live in main memory, so a texture nothing writes again costs twice what it has to. Refused for content that still needs the texels - an indexed texture (its bakes are rebuilt from the indices on every palette change), streaming content, a render target or the shared palette texture. Does nothing on a texture that has already given them up.

void* nCine::RHI::PICA::PicaTexture::MapStreamingTexels(std::int32_t& strideBytes)

Declined here: always returns nullptr (the contract's streaming-texture fast path).

The GPU samples only tiled stores, so there is no linear pitch a CPU writer could fill; the cinematics take the copy-through-a-buffer path and the store is tiled at upload.