Skip to content
Switch branches/tags
Go to file
Cannot retrieve contributors at this time
// This provides a "sane" low-level API for me to render to, kind of inspired
// by Metal, WebGPU and friends. The goal here is to be a good API to write to
// while also allowing me to port to other backends (like WebGPU) in the future.
import type { GfxBuffer, GfxTexture, GfxRenderTarget, GfxSampler, GfxProgram, GfxInputLayout, GfxInputState, GfxRenderPipeline, GfxBindings, GfxResource, GfxReadback } from "./GfxPlatformImpl";
import { GfxFormat } from "./GfxPlatformFormat";
export enum GfxCompareMode {
Never = WebGLRenderingContext.NEVER,
Less = WebGLRenderingContext.LESS,
Equal = WebGLRenderingContext.EQUAL,
LessEqual = WebGLRenderingContext.LEQUAL,
Greater = WebGLRenderingContext.GREATER,
NotEqual = WebGLRenderingContext.NOTEQUAL,
GreaterEqual = WebGLRenderingContext.GEQUAL,
Always = WebGLRenderingContext.ALWAYS,
export enum GfxFrontFaceMode {
CCW = WebGLRenderingContext.CCW,
CW = WebGLRenderingContext.CW,
export const enum GfxCullMode {
export enum GfxBlendFactor {
Zero = WebGLRenderingContext.ZERO,
One = WebGLRenderingContext.ONE,
Src = WebGLRenderingContext.SRC_COLOR,
OneMinusSrc = WebGLRenderingContext.ONE_MINUS_SRC_COLOR,
Dst = WebGLRenderingContext.DST_COLOR,
OneMinusDst = WebGLRenderingContext.ONE_MINUS_DST_COLOR,
SrcAlpha = WebGLRenderingContext.SRC_ALPHA,
OneMinusSrcAlpha = WebGLRenderingContext.ONE_MINUS_SRC_ALPHA,
DstAlpha = WebGLRenderingContext.DST_ALPHA,
OneMinusDstAlpha = WebGLRenderingContext.ONE_MINUS_DST_ALPHA,
export enum GfxBlendMode {
Add = WebGLRenderingContext.FUNC_ADD,
Subtract = WebGLRenderingContext.FUNC_SUBTRACT,
ReverseSubtract = WebGLRenderingContext.FUNC_REVERSE_SUBTRACT,
export const enum GfxWrapMode { Clamp, Repeat, Mirror }
export const enum GfxTexFilterMode { Point, Bilinear }
// TODO(jstpierre): remove NoMip
export const enum GfxMipFilterMode { NoMip, Nearest, Linear }
export const enum GfxPrimitiveTopology { Triangles }
export const enum GfxBufferUsage {
Index = 0x01,
Vertex = 0x02,
Uniform = 0x03,
export const enum GfxBufferFrequencyHint {
Static = 0x01,
Dynamic = 0x02,
export const enum GfxVertexBufferFrequency {
PerVertex = 0x01,
PerInstance = 0x02,
export const enum GfxTextureDimension {
n2D, n2DArray, n3D, Cube,
export const enum GfxTextureUsage {
Sampled = 0x01,
RenderTarget = 0x02,
export const enum GfxChannelWriteMask {
None = 0x00,
Red = 0x01,
Green = 0x02,
Blue = 0x04,
Alpha = 0x08,
RGB = 0x07,
AllChannels = 0x0F,
export enum GfxStencilOp {
Keep = WebGLRenderingContext.KEEP,
Zero = WebGLRenderingContext.ZERO,
Replace = WebGLRenderingContext.REPLACE,
Invert = WebGLRenderingContext.INVERT,
IncrementClamp = WebGLRenderingContext.INCR,
DecrementClamp = WebGLRenderingContext.DECR,
IncrementWrap = WebGLRenderingContext.INCR_WRAP,
DecrementWrap = WebGLRenderingContext.DECR_WRAP,
export interface GfxVertexBufferDescriptor {
buffer: GfxBuffer;
byteOffset: number;
export interface GfxIndexBufferDescriptor extends GfxVertexBufferDescriptor {
export interface GfxVertexAttributeDescriptor {
location: number;
format: GfxFormat;
bufferIndex: number;
bufferByteOffset: number;
export interface GfxInputLayoutBufferDescriptor {
byteStride: number;
frequency: GfxVertexBufferFrequency;
export interface GfxTextureDescriptor {
dimension: GfxTextureDimension;
pixelFormat: GfxFormat;
width: number;
height: number;
depth: number;
numLevels: number;
usage: GfxTextureUsage;
export function makeTextureDescriptor2D(pixelFormat: GfxFormat, width: number, height: number, numLevels: number): GfxTextureDescriptor {
const dimension = GfxTextureDimension.n2D, depth = 1;
const usage = GfxTextureUsage.Sampled;
return { dimension, pixelFormat, width, height, depth, numLevels, usage };
export interface GfxSamplerDescriptor {
wrapS: GfxWrapMode;
wrapT: GfxWrapMode;
wrapQ?: GfxWrapMode;
minFilter: GfxTexFilterMode;
magFilter: GfxTexFilterMode;
mipFilter: GfxMipFilterMode;
minLOD?: number;
maxLOD?: number;
maxAnisotropy?: number;
export interface GfxRenderTargetDescriptor {
pixelFormat: GfxFormat;
width: number;
height: number;
sampleCount: number;
export interface GfxBufferBinding {
buffer: GfxBuffer;
wordCount: number;
export interface GfxSamplerBinding {
gfxTexture: GfxTexture | null;
gfxSampler: GfxSampler | null;
lateBinding: string | null;
export interface GfxBindingLayoutDescriptor {
numUniformBuffers: number;
numSamplers: number;
export interface GfxBindingsDescriptor {
bindingLayout: GfxBindingLayoutDescriptor;
uniformBufferBindings: GfxBufferBinding[];
samplerBindings: GfxSamplerBinding[];
export interface GfxProgramDescriptorSimple {
preprocessedVert: string;
preprocessedFrag: string;
export interface GfxProgramDescriptor extends GfxProgramDescriptorSimple {
ensurePreprocessed(vendorInfo: GfxVendorInfo): void;
associate(device: GfxDevice, program: GfxProgram): void;
export interface GfxInputLayoutDescriptor {
vertexBufferDescriptors: (GfxInputLayoutBufferDescriptor | null)[];
vertexAttributeDescriptors: GfxVertexAttributeDescriptor[];
indexBufferFormat: GfxFormat | null;
export interface GfxChannelBlendState {
blendMode: GfxBlendMode;
blendSrcFactor: GfxBlendFactor;
blendDstFactor: GfxBlendFactor;
export interface GfxAttachmentState {
channelWriteMask: GfxChannelWriteMask;
rgbBlendState: GfxChannelBlendState;
alphaBlendState: GfxChannelBlendState;
export interface GfxMegaStateDescriptor {
attachmentsState: GfxAttachmentState[];
blendConstant: GfxColor;
depthCompare: GfxCompareMode;
depthWrite: boolean;
stencilCompare: GfxCompareMode;
stencilWrite: boolean;
stencilPassOp: GfxStencilOp;
cullMode: GfxCullMode;
frontFace: GfxFrontFaceMode;
polygonOffset: boolean;
export interface GfxRenderPipelineDescriptor {
bindingLayouts: GfxBindingLayoutDescriptor[];
inputLayout: GfxInputLayout | null;
program: GfxProgram;
topology: GfxPrimitiveTopology;
megaStateDescriptor: GfxMegaStateDescriptor;
// Attachment data.
colorAttachmentFormats: (GfxFormat | null)[];
depthStencilAttachmentFormat: GfxFormat | null;
sampleCount: number;
export interface GfxColor {
r: number;
g: number;
b: number;
a: number;
export interface GfxRenderPassDescriptor {
colorAttachment: (GfxRenderTarget | null)[];
colorClearColor: (GfxColor | 'load')[];
colorResolveTo: (GfxTexture | null)[];
depthStencilAttachment: GfxRenderTarget | null;
depthStencilResolveTo: GfxTexture | null;
depthClearValue: number | 'load';
stencilClearValue: number | 'load';
export interface GfxDeviceLimits {
uniformBufferWordAlignment: number;
uniformBufferMaxPageWordSize: number;
readonly supportedSampleCounts: number[];
export interface GfxDebugGroup {
name: string;
drawCallCount: number;
textureBindCount: number;
bufferUploadCount: number;
triangleCount: number;
export const enum GfxViewportOrigin {
export const enum GfxClipSpaceNearZ {
export interface GfxVendorInfo {
readonly platformString: string;
readonly glslVersion: string;
readonly explicitBindingLocations: boolean;
readonly separateSamplerTextures: boolean;
readonly viewportOrigin: GfxViewportOrigin;
readonly clipSpaceNearZ: GfxClipSpaceNearZ;
readonly supportsSyncPipelineCompilation: boolean;
export type GfxPlatformFramebuffer = WebGLFramebuffer;
// Viewport in normalized coordinate space, from 0 to 1.
export interface GfxNormalizedViewportCoords {
x: number;
y: number;
w: number;
h: number;
export interface GfxSwapChain {
// WebXR requires presenting to a platform-defined framebuffer, for all that is unholy.
// This hopefully is less terrible in the future. See
configureSwapChain(width: number, height: number, platformFramebuffer?: GfxPlatformFramebuffer): void;
getDevice(): GfxDevice;
getCanvas(): HTMLCanvasElement | OffscreenCanvas;
getOnscreenTexture(): GfxTexture;
present(): void;
createWebXRLayer(webXRSession: XRSession): XRWebGLLayer;
export interface GfxRenderPass {
// State management.
setViewport(x: number, y: number, w: number, h: number): void;
setScissor(x: number, y: number, w: number, h: number): void;
setPipeline(pipeline: GfxRenderPipeline): void;
setBindings(bindingLayoutIndex: number, bindings: GfxBindings, dynamicByteOffsets: number[]): void;
setInputState(inputState: GfxInputState | null): void;
setStencilRef(value: number): void;
setDebugPointer(value: any): void;
// Draw commands.
draw(vertexCount: number, firstVertex: number): void;
drawIndexed(indexCount: number, firstIndex: number): void;
drawIndexedInstanced(indexCount: number, firstIndex: number, instanceCount: number): void;
export type GfxPass = GfxRenderPass;
* GfxDevice represents a "virtual GPU"; this is something that, in the abstract, has a bunch of resources
* and can execute passes. In the concrete, this is a wrapper around a CanvasWebGL2RenderingContext for the
* WebGL 2 backend, or a GPUDevice for the WebGPU backend.
* A bit about the design of this API; all resources are "opaque", meaning you cannot look at the
* implementation details or underlying fields of the resources, and most objects cannot have their
* creation parameters modified after they are created. So, while buffers and textures can have their
* contents changed through data upload passes, they cannot be resized after creation. Create a new object
* and destroy the old one if you wish to "resize" it.
* To upload data to the GPU, call either {@see uploadBufferData} or {@see uploadTextureData}. Note that
* this happens on the GPU timeline. Where possible, do try to upload data at the beginning of the frame.
* There might be additional support for more passes in the future.
export interface GfxDevice {
createBuffer(wordCount: number, usage: GfxBufferUsage, hint: GfxBufferFrequencyHint): GfxBuffer;
createTexture(descriptor: GfxTextureDescriptor): GfxTexture;
createSampler(descriptor: GfxSamplerDescriptor): GfxSampler;
createRenderTarget(descriptor: GfxRenderTargetDescriptor): GfxRenderTarget;
createRenderTargetFromTexture(texture: GfxTexture): GfxRenderTarget;
createProgram(program: GfxProgramDescriptor): GfxProgram;
createProgramSimple(program: GfxProgramDescriptorSimple): GfxProgram;
createBindings(bindingsDescriptor: GfxBindingsDescriptor): GfxBindings;
createInputLayout(inputLayoutDescriptor: GfxInputLayoutDescriptor): GfxInputLayout;
createInputState(inputLayout: GfxInputLayout, buffers: (GfxVertexBufferDescriptor | null)[], indexBuffer: GfxIndexBufferDescriptor | null): GfxInputState;
createRenderPipeline(descriptor: GfxRenderPipelineDescriptor): GfxRenderPipeline;
createReadback(elemCount: number): GfxReadback;
* Destructors. You *must* call these on resources you create; they will not GC naturally. Call checkForLeaks()
* to ensure that you are not leaking any resources. (In the noclip codebase, this happens automatically if you
* set loadSceneDelta to 0 and switch scenes).
destroyBuffer(o: GfxBuffer): void;
destroyTexture(o: GfxTexture): void;
destroySampler(o: GfxSampler): void;
destroyRenderTarget(o: GfxRenderTarget): void;
destroyProgram(o: GfxProgram): void;
destroyBindings(o: GfxBindings): void;
destroyInputLayout(o: GfxInputLayout): void;
destroyInputState(o: GfxInputState): void;
destroyRenderPipeline(o: GfxRenderPipeline): void;
destroyReadback(o: GfxReadback): void;
// Command submission.
createRenderPass(renderPassDescriptor: GfxRenderPassDescriptor): GfxRenderPass;
// Consumes and destroys the pass.
submitPass(o: GfxPass): void;
// Data submission
uploadBufferData(buffer: GfxBuffer, dstByteOffset: number, data: Uint8Array, srcByteOffset?: number, byteCount?: number): void;
uploadTextureData(texture: GfxTexture, firstMipLevel: number, levelDatas: ArrayBufferView[]): void;
// Readback system.
readPixelFromTexture(o: GfxReadback, dstOffset: number, a: GfxTexture, x: number, y: number): void;
submitReadback(o: GfxReadback): void;
queryReadbackFinished(dst: Uint32Array, dstOffs: number, o: GfxReadback): boolean;
// Information queries.
queryLimits(): GfxDeviceLimits;
queryTextureFormatSupported(format: GfxFormat): boolean;
queryPipelineReady(o: GfxRenderPipeline): boolean;
queryPlatformAvailable(): boolean;
queryVendorInfo(): GfxVendorInfo;
queryRenderPass(o: GfxRenderPass): Readonly<GfxRenderPassDescriptor>;
queryRenderTarget(o: GfxRenderTarget): Readonly<GfxRenderTargetDescriptor>;
// Debugging.
setResourceName(o: GfxResource, s: string): void;
setResourceLeakCheck(o: GfxResource, v: boolean): void;
checkForLeaks(): void;
programPatched(o: GfxProgram, descriptor: GfxProgramDescriptorSimple): void;
pushDebugGroup(debugGroup: GfxDebugGroup): void;
popDebugGroup(): void;
export type { GfxBuffer, GfxTexture, GfxRenderTarget, GfxSampler, GfxProgram, GfxInputLayout, GfxInputState, GfxRenderPipeline, GfxBindings };
export { GfxFormat };