Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commit3bb1da2647

README.md

compute Module

Public Luau surface over the __compute Internal FFI namespace — GPU compute pipelines.

Purpose

A compute shader is a .computeShader asset, and everything you do with one you do through a resolved reference to it. This module carries what is left: the readback poll, and the name-keyed entry points kept working for content written before the reference surface existed.

Usage

local double = asset.resolve("@builtin::shaders.compute_double", "computeShader")

local scratch = double:createBuffer("scratch", { size = 4096, readback = true })
scratch:write({ 1.0, 2.0, 3.0, 4.0 })

double:dispatch({ buffers = { scratch }, workgroups = { 1 } })

local key = scratch:read()
while not compute.isReadbackReady(key) do task.wait(0) end
local floats = compute.getReadbackResult(key)

asset.resolve records your script's dependency on the shader — which is what carries the shader along when the world is packed, pulled or published. A name passed to one of the entry points below records nothing, and the shader is left behind.

Is it compiled yet?

compute.programState(ref) says where a shader's compiled program stands: "absent" (nothing held under this key and nothing in flight), "pending" (a registration or compile was asked for and has not reached the device — a recompile of a resident program reads pending too), "ready" (compiled and resident, so a dispatch binds it), or "failed", with the reason as a second return.

if compute.programState(shaderRef) == "ready" then
    shaderRef:dispatch({ buffers = { buf }, workgroups = { 1 } })
end

ref:compile() queues the compile and the pipeline is built on the render side frames later, so that call cannot answer this. A dispatch issued before the program is ready is held behind the compile and dropped if it never lands, and a readback taken then reports the buffer as it was.

Is anything broken?

compute.failing() lists every (shader, target) pair whose most recent dispatch failed, with the reason. An empty result means every dispatch the engine has been given is running. For one shader, ref:status() reports each of its targets, running or not.

for _, d in ipairs(compute.failing()) do
    print(d.shader, d.target, d.failures, d.lastError)
end

A dispatch is recorded into a command encoder frames after the call that asked for it returned, so the call answers that it queued the dispatch and these two answer what the dispatch did. A zero in any workgroups dimension is one of the things they report: it is refused, and a count computed from how much data there is passes through math.max(1, ...) so an empty set dispatches one workgroup rather than none.

Exports

  • Health: programState, failing.
  • Readback: isReadbackReady, readbackState, getReadbackResult, getReadbackResultU32, getReadbackResultBytes. A handle answers a read itself — buf:read(offset, len) gives a Readback with ready, state and the three drains on it.
  • Deprecated, name-keyed: registerShader, destroyShader, registerShaderEx, destroyShaderEx, compile, compileByName, dispatch, dispatchOnVertices, dispatchEx, setParam. Each has a method on the .computeShader reference — see types/computeShader.
  • Deprecated, name-keyed buffers: createBuffer(name, { size, readback? }), destroyBuffer, readBuffer, writeBuffer(name, data, offset?), writeBufferU32, writeBufferBytes. Each reaches the substrate under the name a dispatch binds it by; hold the handle from substrate.createBuffer or ref:createBuffer instead.