simvx.graphics.materials.texture¶
Texture loading and bindless descriptor array management.
One canonical TextureManager serves both the Vulkan and web backends:
the only thing that differs between them is the _TextureRegistrar that
turns RGBA pixels into a backend-specific texture id. On the web path the
manager also retains pixel data so prepare_2d_overlays can re-ship it
over the drain channel; on the desktop path that’s skipped (retain_pixels=False)
to avoid doubling VRAM.
Module Contents¶
Classes¶
Loads textures via a backend registrar and caches by source identity. |
Data¶
API¶
- simvx.graphics.materials.texture.__all__¶
[‘TextureManager’]
- simvx.graphics.materials.texture.log¶
‘getLogger(…)’
- simvx.graphics.materials.texture.TextureSource¶
None
- class simvx.graphics.materials.texture.TextureManager(registrar: simvx.graphics.materials.texture._TextureRegistrar, *, retain_pixels: bool = False)¶
Loads textures via a backend registrar and caches by source identity.
Desktop Vulkan:
TextureManager(engine): pixels uploaded and forgotten. Web:TextureManager(renderer, retain_pixels=True): pixels retained soprepare_2d_overlayscan re-ship them over the drain channel on demand.Initialization
- resolve(source: simvx.graphics.materials.texture.TextureSource | None, *, filter: str = 'linear', premultiply_alpha: bool = False, colour_space: str = 'srgb', mipmaps: bool = False) int¶
Resolve any supported texture source to a backend texture index.
Returns -1 for
None, empty strings, or sources that cannot be resolved (e.g. a path that does not exist). All callers that accept a user-providedtextureproperty should go through this method.Supported sources: *
str/pathlib.Path: file on disk (PNG / JPG / …) *bytes: raw encoded image data (PNG / JPG) *numpy.ndarray: RGBA uint8 pixels, shape(H, W, 4)* :class:~simvx.core.graphics.Texture: an owned texture resource, keyed by its identity rather than by its source, so a mutated pixel array re-uploads (see :meth:refresh) into the same slot, and that slot belongs to the resource alone – it is neither shared with the same file loaded plainly nor kept alive after the resource dies. Each sampling setting the resource STATES overrides the matching keyword here; the ones it leaves unset take the keyword, so a resource behaves like the raw source it wraps unless its author said otherwise.filterselects the sampler bound at the bindless slot:"linear"(default) or"nearest". Each (source, filter, premultiply_alpha) tuple gets its own slot so the same source can be drawn smoothly somewhere, crisply elsewhere, with or without alpha premultiplication, all without re-uploading pixels.Args: filter: Sampler filter mode:
"linear"or"nearest". premultiply_alpha: When True, multiply RGB by alpha before GPU upload (matchesimage_loader.premultiply_alpha_rgba). Fixes halo artefacts on alpha-blended PNGs whose transparent pixels carry stale RGB. Default False: existing visual snapshots use straight alpha. mipmaps: When True, ask the registrar to generate a full runtime mip chain (desktop blit chain; web fullscreen-sample chain). Registrars without the capability fall back to a single mip. Default False keeps every existing upload byte-identical.
- release_resource(uid: int) None¶
Give a dead :class:
Texture’s slot back to the backend.Armed as a
weakref.finalizeon the resource itself: a game that builds a texture per level or per entity gets its bindless slots back when those textures go, which a raw ndarray source only manages because the array’s own lifetime stands in for it.
- refresh(texture: simvx.core.graphics.Texture) None¶
Re-upload a texture resource whose pixels changed. Called by
Texture.update().A no-op for a resource this manager never resolved, which is what lets a texture be shared across backends without either one guessing.
- drain_updated_slots() set[int]¶
Take the set of slots re-uploaded since the last call.
The web backend re-ships their pixels over the resource channel; on desktop the image is already written and nothing reads this.
- load(path: str | pathlib.Path, *, filter: str = 'linear', premultiply_alpha: bool = False, colour_space: str = 'srgb', mipmaps: bool = False) int¶
Load a texture from disk. Cached by (resolved path, filter, premul, colour_space).
.ddsand.ktx2files take the block-compressed path (BC1-BC7) on backends that expose a compressed registrar; everything else takes the RGBA8 path.colour_space("srgb"default /"linear") selects the sampled view format on the RGBA8 path; compressed files carry their own sRGB flag in the container format.
- load_from_bytes(data: bytes, *, filter: str = 'linear', premultiply_alpha: bool = False, colour_space: str = 'srgb', mipmaps: bool = False) int¶
Load a texture from in-memory image bytes. Cached by (content, filter, premul).
A leading
b'DDS 'magic or the 12-byte KTX2 identifier routes to the block-compressed path; PNG/JPG bytes take the RGBA8 path unchanged.
- load_from_array(pixels: numpy.ndarray, *, filter: str = 'linear', premultiply_alpha: bool = False, colour_space: str = 'srgb', mipmaps: bool = False) int¶
Upload an RGBA uint8 ndarray of shape
(H, W, 4).Cached by
(id(pixels), filter, premultiply_alpha): the same ndarray with the same triple returns the same index; any difference allocates a fresh slot so a single source can be drawn through multiple sampler/premultiplication paths without collision.
- load_if_exists(path: str | pathlib.Path, *, filter: str = 'linear', premultiply_alpha: bool = False, colour_space: str = 'srgb', mipmaps: bool = False) int¶
Load a texture if the file exists. Returns -1 if not found.
- get_texture_size(tex_idx: int) tuple[int, int]¶
Return (width, height) for a loaded texture index. (0, 0) if unknown.
- get_pixels(tex_id: int) numpy.ndarray | None¶
Return retained RGBA pixels for
tex_id, or None.Only populated when the manager was constructed with
retain_pixels=True. Used by the web runtime to re-ship 2D overlay pixels over the drain channel without the browser having to fetch them back out.
- property count: int¶
Number of unique textures loaded.
- destroy() None¶
Clear all caches (GPU resources are owned by the backend).
- release(idx: int, cache_key: str | None = None, source_id: int | None = None) None¶
Reclaim a texture slot + drop cache bookkeeping.
Called by the weakref.finalize attached in
load_from_arraywhen the source ndarray is GC’d. Backend unregister is delegated to the registrar when it exposesunregister_texture(desktop Engine); web registrars may opt out.