Venus Lua API › Rendering

Texture

GPU texture handle, plus CPU-editable dynamic textures.

authored lua/venus_lua/src/lua_texture.rs

Texture

A handle to a GPU texture. Most textures are loaded assets you look up by name and read dimensions
from. Dynamic textures (created via CreateDynamic / CreateDynamic3D) add a CPU-side staging
copy you can edit pixel-by-pixel and push to the GPU with Apply.

Static calls use the type table (Texture.Get(...)); instance methods use colon syntax on a handle
(tex:SetPixel(...)).

Types

Texture — an opaque handle (userdata) to a texture. A handle may be null or stale (its
texture destroyed); it is safe to keep and pass around. tostring(tex) yields Texture{name}, or
Texture{NULL} if dead.

The pixel-editing methods only apply to dynamic textures; on a non-dynamic or dead texture they
log InvalidArgument and no-op. Pixel coordinates are 0-based texel indices (x in 0 .. Width()-1, y in 0 .. Height()-1, z in 0 .. Depth()-1); an out-of-range coordinate is
rejected by the manager (logs InvalidArgument). Color components are integers in 0–255 (see
the known issue below), not normalized floats.

Creation & Destruction

Texture.Get( string name ) : Texture
Looks up a texture by its unique name. Returns nil if none exists (no warning — a miss is a valid result).

Texture.Exists( Texture texture ) : boolean
Returns true if the handle refers to a live texture. Safe on null / stale / foreign handles
(returns false, never warns).

Texture.CreateDynamic( string name, integer width, integer height, integer format ) : Texture
Creates a 2D dynamic (CPU-editable) texture. format is a Texture.PixelFormat value. Returns the
new handle, or nil (logs InvalidArgument) if the format is unknown, the name is taken, or GPU
allocation fails.

Texture.CreateDynamic3D( string name, integer width, integer height, integer depth, integer format ) : Texture
Creates a 3D dynamic texture (a 2D texture when depth <= 1). format is a Texture.PixelFormat
value. Returns the new handle, or nil (logs InvalidArgument) on failure.

tex:Destroy( ) : void
Releases the GPU image (if any) and destroys the texture. No-op on a null handle.

Functions

Read-only queries. Instance getters log NotFound and return nil on a dead handle unless noted.

Texture.IsDynamic( Texture texture ) : boolean
true if texture is a dynamic (CPU-editable) texture. Safe on null / foreign handles (returns
false).

tex:ID( ) : string
The texture’s unique name.

tex:Width( ) : integer
Width in texels.

tex:Height( ) : integer
Height in texels.

tex:Depth( ) : integer
Depth in texels (1 for 2D textures). Returns 0 (not nil) on error.

tex:AspectRatio( ) : number
Width / height. Returns nil on error.

tex:GetPixel( integer x, integer y ) : integer, integer, integer, integer
Reads a texel from the current frame’s CPU staging copy of a dynamic texture as four 0–255
components r, g, b, a. Returns 0, 0, 0, 0 and logs InvalidArgument on error (non-dynamic, dead,
or out-of-range coordinate).

tex:GetPixel3D( integer x, integer y, integer z ) : integer, integer, integer, integer
As GetPixel, for a 3D dynamic texture. Returns 0, 0, 0, 0 on error.

tex:GetPixelData( ) : string
Returns the entire CPU staging buffer as a raw byte string — the texels packed in the texture’s
pixel format (e.g. 4 bytes/texel for RGBA8), row-major. Returns an empty string (logs NotFound)
on error.

Methods

Mutating actions on dynamic textures. They log InvalidArgument on a non-dynamic or dead
texture. Edits touch the current frame’s CPU staging copy; call Apply to schedule the GPU upload.

tex:SetPixel( integer x, integer y, integer r, integer g, integer b, integer a ) : void
Writes one RGBA texel into the staging copy. Components are integers (see the known issue).

tex:SetPixel3D( integer x, integer y, integer z, integer r, integer g, integer b, integer a ) : void
Writes one RGBA texel into a 3D dynamic texture.

tex:SetPixelData( string bytes ) : void
Overwrites the whole staging buffer from a raw byte string (must match the buffer’s size and format
layout, else InvalidArgument).

tex:SetPixelDataRange( integer offset, string bytes ) : void
Writes bytes into the staging buffer starting at byte offset. A negative offset logs
InvalidArgument and is rejected; an offset + length past the buffer end is rejected by the manager.

tex:Apply( ) : void
Drains the accumulated dirty region and marks it on the GPU image so the backend can issue a
targeted subregion copy for the current frame. No-op on a null handle or a texture with no GPU image.

Known issue — components are integers cast to u8, no clamp (verified in source).
SetPixel / SetPixel3D read each of r, g, b, a with luaL_checkinteger(...) as u8
(lua_texture.rs:387-390, :433-436). Two consequences: (1) they are integers, not
normalized floats — pass 255, not 1.0 (a fractional Lua number such as 0.5 raises “number
has no integer representation”); (2) values outside 0–255 wrap modulo 256 rather than
clamping (256 -> 0, -1 -> 255). Clamp/round in Lua (e.g. math.floor into 0..255) before
calling.

Constants

Texture.PixelFormat — pixel format discriminants passed to CreateDynamic / CreateDynamic3D.
An unrecognized value fails creation with InvalidArgument.

None (0), BGRA8_sRGB (1), R8 (2), RG8 (3), R16F (4), BGRA8 (5), RG16F (6),
R32F (7), RGBA16F (8), RG32F (9), D32 (10), D32_S8 (11), BC1 (12), BC1_sRGB (13),
BC2 (14), BC2_sRGB (15), BC3 (16), BC3_sRGB (17), BC4 (18), BC5 (19), BC6 (20),
BC7 (21), BC7_sRGB (22), RGBA8 (23), RGBA8_sRGB (24).

Example

-- Walk a colored texel across a 64x64 dynamic texture each frame.
-- (Adapted from data/scripts/dyn_texture_demo.lua.)
local px, py, frame = 0, 0, 0

function Script.Update(self)
    local tex = Texture.Get("dyn_demo")
    if not tex then return end

    -- Integer 0-255 components (math.floor keeps them in range).
    local r = math.floor((px / 64) * 255)
    local g = math.floor((py / 64) * 255)
    local b = frame % 256
    tex:SetPixel(px, py, r, g, b, 255)
    tex:Apply()

    px = px + 1
    if px >= 64 then px, py = 0, (py + 1) % 64 end
    frame = frame + 1
end