@luma.gl/webgpu
Advanced tools
Comparing version 9.1.0-alpha.19 to 9.1.0-beta.1
@@ -104,1 +104,2 @@ // luma.gl | ||
*/ | ||
//# sourceMappingURL=accessor-to-format.js.map |
@@ -11,1 +11,2 @@ // luma.gl | ||
} | ||
//# sourceMappingURL=convert-texture-format.js.map |
@@ -11,3 +11,5 @@ import type { ComputeShaderLayout, BindingDeclaration, Binding } from '@luma.gl/core'; | ||
export declare function getBindGroup(device: GPUDevice, bindGroupLayout: GPUBindGroupLayout, shaderLayout: ComputeShaderLayout, bindings: Record<string, Binding>): GPUBindGroup; | ||
export declare function getShaderLayoutBinding(shaderLayout: ComputeShaderLayout, bindingName: string): BindingDeclaration | null; | ||
export declare function getShaderLayoutBinding(shaderLayout: ComputeShaderLayout, bindingName: string, options?: { | ||
ignoreWarnings?: boolean; | ||
}): BindingDeclaration | null; | ||
//# sourceMappingURL=get-bind-group.d.ts.map |
@@ -26,5 +26,5 @@ // luma.gl | ||
} | ||
export function getShaderLayoutBinding(shaderLayout, bindingName) { | ||
export function getShaderLayoutBinding(shaderLayout, bindingName, options) { | ||
const bindingLayout = shaderLayout.bindings.find(binding => binding.name === bindingName || `${binding.name}uniforms` === bindingName.toLocaleLowerCase()); | ||
if (!bindingLayout) { | ||
if (!bindingLayout && !options?.ignoreWarnings) { | ||
log.warn(`Binding ${bindingName} not set: Not found in shader layout.`)(); | ||
@@ -41,10 +41,17 @@ } | ||
for (const [bindingName, value] of Object.entries(bindings)) { | ||
const bindingLayout = getShaderLayoutBinding(shaderLayout, bindingName); | ||
let bindingLayout = getShaderLayoutBinding(shaderLayout, bindingName); | ||
if (bindingLayout) { | ||
entries.push(getBindGroupEntry(value, bindingLayout.location)); | ||
} | ||
// TODO - hack to automatically bind samplers to supplied texture default samplers | ||
bindingLayout = getShaderLayoutBinding(shaderLayout, `${bindingName}Sampler`, { | ||
ignoreWarnings: true | ||
}); | ||
if (bindingLayout) { | ||
entries.push(getBindGroupEntry(value, bindingLayout.location, { sampler: true })); | ||
} | ||
} | ||
return entries; | ||
} | ||
function getBindGroupEntry(binding, index) { | ||
function getBindGroupEntry(binding, index, options) { | ||
if (binding instanceof Buffer) { | ||
@@ -65,2 +72,8 @@ return { | ||
else if (binding instanceof Texture) { | ||
if (options?.sampler) { | ||
return { | ||
binding: index, | ||
resource: binding.sampler.handle | ||
}; | ||
} | ||
return { | ||
@@ -73,1 +86,2 @@ binding: index, | ||
} | ||
//# sourceMappingURL=get-bind-group.js.map |
@@ -127,7 +127,7 @@ // luma.gl | ||
if (!attribute) { | ||
log.warn(`Unknown attribute ${name}`)(); | ||
log.warn(`Supplied attribute not present in shader layout: ${name}`)(); | ||
return null; | ||
} | ||
if (attributeNames.has(name)) { | ||
throw new Error(`Duplicate attribute ${name}`); | ||
throw new Error(`Found multiple entries for attribute: ${name}`); | ||
} | ||
@@ -137,1 +137,2 @@ attributeNames.add(name); | ||
} | ||
//# sourceMappingURL=get-vertex-buffer-layout.js.map |
@@ -213,1 +213,2 @@ // luma.gl | ||
} | ||
//# sourceMappingURL=webgpu-parameters.js.map |
@@ -128,1 +128,2 @@ // luma.gl | ||
*/ | ||
//# sourceMappingURL=webgpu-buffer.js.map |
@@ -102,1 +102,2 @@ // luma.gl | ||
*/ | ||
//# sourceMappingURL=webgpu-command-encoder.js.map |
@@ -81,1 +81,2 @@ // luma.gl | ||
} | ||
//# sourceMappingURL=webgpu-compute-pass.js.map |
@@ -50,1 +50,2 @@ // luma.gl | ||
} | ||
//# sourceMappingURL=webgpu-compute-pipeline.js.map |
@@ -41,1 +41,2 @@ // luma.gl | ||
} | ||
//# sourceMappingURL=webgpu-external-texture.js.map |
@@ -23,1 +23,2 @@ // luma.gl | ||
} | ||
//# sourceMappingURL=webgpu-framebuffer.js.map |
@@ -28,1 +28,2 @@ // luma.gl | ||
} | ||
//# sourceMappingURL=webgpu-query-set.js.map |
@@ -32,3 +32,9 @@ // luma.gl | ||
} | ||
this.device.handle.pushErrorScope('validation'); | ||
this.handle = this.props.handle || device.commandEncoder.beginRenderPass(renderPassDescriptor); | ||
this.device.handle.popErrorScope().then((error) => { | ||
if (error) { | ||
log.error(`${this} creation failed:\n"${error.message}"`, this)(); | ||
} | ||
}); | ||
this.handle.label = this.props.id; | ||
@@ -154,1 +160,2 @@ log.groupCollapsed(3, `new WebGPURenderPass(${this.id})`)(); | ||
} | ||
//# sourceMappingURL=webgpu-render-pass.js.map |
@@ -15,2 +15,3 @@ import type { Binding, RenderPass, VertexArray } from '@luma.gl/core'; | ||
private _bindGroup; | ||
get [Symbol.toStringTag](): string; | ||
constructor(device: WebGPUDevice, props: RenderPipelineProps); | ||
@@ -17,0 +18,0 @@ destroy(): void; |
@@ -18,2 +18,5 @@ // luma.gl MIT license | ||
_bindGroup = null; | ||
get [Symbol.toStringTag]() { | ||
return 'WebGPURenderPipeline'; | ||
} | ||
constructor(device, props) { | ||
@@ -28,5 +31,12 @@ super(device, props); | ||
log.groupEnd(1)(); | ||
this.device.handle.pushErrorScope('validation'); | ||
this.handle = this.device.handle.createRenderPipeline(descriptor); | ||
this.device.handle.popErrorScope().then((error) => { | ||
if (error) { | ||
log.error(`${this} creation failed:\n"${error.message}"`, this, this.props.vs?.source)(); | ||
} | ||
}); | ||
} | ||
this.handle.label = this.props.id; | ||
// Note: Often the same shader in WebGPU | ||
this.vs = props.vs; | ||
@@ -46,2 +56,8 @@ this.fs = props.fs; | ||
setBindings(bindings) { | ||
// Invalidate the cached bind group if any value has changed | ||
for (const [name, binding] of Object.entries(bindings)) { | ||
if (this._bindings[name] !== binding) { | ||
this._bindGroup = null; | ||
} | ||
} | ||
Object.assign(this._bindings, bindings); | ||
@@ -53,3 +69,9 @@ } | ||
// Set pipeline | ||
this.device.handle.pushErrorScope('validation'); | ||
webgpuRenderPass.handle.setPipeline(this.handle); | ||
this.device.handle.popErrorScope().then((error) => { | ||
if (error) { | ||
log.error(`${this} setPipeline failed:\n"${error.message}"`, this)(); | ||
} | ||
}); | ||
// Set bindings (uniform buffers, textures etc) | ||
@@ -99,2 +121,13 @@ const bindGroup = this._getBindGroup(); | ||
}; | ||
// Populate color targets | ||
// TODO - at the moment blend and write mask are only set on the first target | ||
const targets = []; | ||
if (this.props.colorAttachmentFormats) { | ||
for (const format of this.props.colorAttachmentFormats) { | ||
targets.push(format ? { format: getWebGPUTextureFormat(format) } : null); | ||
} | ||
} | ||
else { | ||
targets.push({ format: getWebGPUTextureFormat(this.device.preferredColorFormat) }); | ||
} | ||
// Set up the fragment stage | ||
@@ -104,8 +137,3 @@ const fragment = { | ||
entryPoint: this.props.fragmentEntryPoint || 'main', | ||
targets: [ | ||
{ | ||
// TODO exclamation mark hack! | ||
format: getWebGPUTextureFormat(this.device.getCanvasContext().format) | ||
} | ||
] | ||
targets | ||
}; | ||
@@ -121,2 +149,9 @@ // Create a partially populated descriptor | ||
}; | ||
// Set depth format if required, defaulting to the preferred depth format | ||
const depthFormat = this.props.depthStencilAttachmentFormat || this.device.preferredDepthFormat; | ||
if (this.props.parameters.depthWriteEnabled) { | ||
descriptor.depthStencil = { | ||
format: getWebGPUTextureFormat(depthFormat) | ||
}; | ||
} | ||
// Set parameters on the descriptor | ||
@@ -169,1 +204,2 @@ applyParametersToRenderPipelineDescriptor(descriptor, this.props.parameters); | ||
*/ | ||
//# sourceMappingURL=webgpu-render-pipeline.js.map |
@@ -36,1 +36,2 @@ // luma.gl, MIT license | ||
} | ||
//# sourceMappingURL=webgpu-sampler.js.map |
@@ -12,8 +12,7 @@ import type { ShaderProps, CompilerMessage } from '@luma.gl/core'; | ||
get asyncCompilationStatus(): Promise<any>; | ||
_checkCompilationError(errorScope: Promise<GPUError | null>): Promise<void>; | ||
_checkCompilationError(): Promise<void>; | ||
destroy(): void; | ||
/** Returns compilation info for this shader */ | ||
getCompilationInfo(): Promise<readonly CompilerMessage[]>; | ||
protected createHandle(): GPUShaderModule; | ||
} | ||
//# sourceMappingURL=webgpu-shader.d.ts.map |
@@ -14,6 +14,15 @@ // luma.gl | ||
this.device = device; | ||
const isGLSL = props.source.includes('#version'); | ||
if (this.props.language === 'glsl' || isGLSL) { | ||
throw new Error('GLSL shaders are not supported in WebGPU'); | ||
} | ||
this.device.handle.pushErrorScope('validation'); | ||
this.handle = this.props.handle || this.createHandle(); | ||
this.handle = this.props.handle || this.device.handle.createShaderModule({ code: props.source }); | ||
this.device.handle.popErrorScope().then((error) => { | ||
if (error) { | ||
log.error(`${this} creation failed:\n"${error.message}"`, this, this.props.source)(); | ||
} | ||
}); | ||
this.handle.label = this.props.id; | ||
this._checkCompilationError(this.device.handle.popErrorScope()); | ||
this._checkCompilationError(); | ||
} | ||
@@ -23,12 +32,12 @@ get asyncCompilationStatus() { | ||
} | ||
async _checkCompilationError(errorScope) { | ||
const error = (await errorScope); | ||
if (error) { | ||
// The `Shader` base class will determine if debug window should be opened based on props | ||
this.debugShader(); | ||
const shaderLog = await this.getCompilationInfo(); | ||
log.error(`Shader compilation error: ${error.message}`, shaderLog)(); | ||
async _checkCompilationError() { | ||
const shaderLog = await this.getCompilationInfo(); | ||
const hasErrors = Boolean(shaderLog.find(msg => msg.type === 'error')); | ||
this.compilationStatus = hasErrors ? 'error' : 'success'; | ||
this.debugShader(); | ||
if (this.compilationStatus === 'error') { | ||
log.error(`Shader compilation error`, shaderLog)(); | ||
// Note: Even though this error is asynchronous and thrown after the constructor completes, | ||
// it will result in a useful stack trace leading back to the constructor | ||
throw new Error(`Shader compilation error: ${error.message}`); | ||
// throw new Error(`Shader compilation error`); | ||
} | ||
@@ -47,11 +56,3 @@ } | ||
} | ||
// PRIVATE METHODS | ||
createHandle() { | ||
const { source } = this.props; | ||
const isGLSL = source.includes('#version'); | ||
if (this.props.language === 'glsl' || isGLSL) { | ||
throw new Error('GLSL shaders are not supported in WebGPU'); | ||
} | ||
return this.device.handle.createShaderModule({ code: source }); | ||
} | ||
} | ||
//# sourceMappingURL=webgpu-shader.js.map |
@@ -36,1 +36,2 @@ // luma.gl | ||
} | ||
//# sourceMappingURL=webgpu-texture-view.js.map |
@@ -161,1 +161,2 @@ import { Texture } from '@luma.gl/core'; | ||
} | ||
//# sourceMappingURL=webgpu-texture.js.map |
@@ -41,3 +41,3 @@ // luma.gl | ||
// Note we can't unset an index buffer | ||
log.warn('setting index buffer', webgpuIndexBuffer?.handle, webgpuIndexBuffer?.indexType)(); | ||
log.info(3, 'setting index buffer', webgpuIndexBuffer?.handle, webgpuIndexBuffer?.indexType)(); | ||
webgpuRenderPass.handle.setIndexBuffer(webgpuIndexBuffer?.handle, | ||
@@ -50,3 +50,3 @@ // @ts-expect-error TODO - we must enforce type | ||
if (webgpuBuffer?.handle) { | ||
log.warn(`setting vertex buffer ${location}`, webgpuBuffer?.handle)(); | ||
log.info(3, `setting vertex buffer ${location}`, webgpuBuffer?.handle)(); | ||
webgpuRenderPass.handle.setVertexBuffer(location, webgpuBuffer?.handle); | ||
@@ -71,1 +71,2 @@ } | ||
} | ||
//# sourceMappingURL=webgpu-vertex-array.js.map |
@@ -65,1 +65,2 @@ // luma.gl | ||
export const webgpuAdapter = new WebGPUAdapter(); | ||
//# sourceMappingURL=webgpu-adapter.js.map |
@@ -1,2 +0,2 @@ | ||
import type { Texture, TextureFormat, CanvasContextProps } from '@luma.gl/core'; | ||
import type { DepthStencilTextureFormat, CanvasContextProps } from '@luma.gl/core'; | ||
import { CanvasContext } from '@luma.gl/core'; | ||
@@ -13,8 +13,5 @@ import { WebGPUDevice } from "./webgpu-device.js"; | ||
readonly device: WebGPUDevice; | ||
readonly gpuCanvasContext: GPUCanvasContext; | ||
/** Format of returned textures: "bgra8unorm", "rgba8unorm", "rgba16float". */ | ||
readonly format: TextureFormat; | ||
/** Default stencil format for depth textures */ | ||
readonly depthStencilFormat: TextureFormat; | ||
readonly handle: GPUCanvasContext; | ||
private depthStencilAttachment; | ||
get [Symbol.toStringTag](): string; | ||
constructor(device: WebGPUDevice, adapter: GPUAdapter, props: CanvasContextProps); | ||
@@ -24,5 +21,7 @@ /** Destroy any textures produced while configured and remove the context configuration. */ | ||
/** Update framebuffer with properly resized "swap chain" texture views */ | ||
getCurrentFramebuffer(): WebGPUFramebuffer; | ||
getCurrentFramebuffer(options?: { | ||
depthStencilFormat?: DepthStencilTextureFormat | false; | ||
}): WebGPUFramebuffer; | ||
/** Resizes and updates render targets if necessary */ | ||
update(): void; | ||
updateSize(size: [newWidth: number, newHeight: number]): void; | ||
resize(options?: { | ||
@@ -36,4 +35,4 @@ width?: number; | ||
/** We build render targets on demand (i.e. not when size changes but when about to render) */ | ||
_createDepthStencilAttachment(): Texture; | ||
_createDepthStencilAttachment(depthStencilFormat: DepthStencilTextureFormat): WebGPUTexture; | ||
} | ||
//# sourceMappingURL=webgpu-canvas-context.d.ts.map |
// luma.gl | ||
// SPDX-License-Identifier: MIT | ||
// Copyright (c) vis.gl contributors | ||
import { CanvasContext, log } from '@luma.gl/core'; | ||
import { getWebGPUTextureFormat } from "./helpers/convert-texture-format.js"; | ||
import { CanvasContext, Texture, log } from '@luma.gl/core'; | ||
import { WebGPUFramebuffer } from "./resources/webgpu-framebuffer.js"; | ||
@@ -14,43 +13,36 @@ /** | ||
device; | ||
gpuCanvasContext; | ||
/** Format of returned textures: "bgra8unorm", "rgba8unorm", "rgba16float". */ | ||
format = navigator.gpu.getPreferredCanvasFormat(); | ||
/** Default stencil format for depth textures */ | ||
depthStencilFormat = 'depth24plus'; | ||
handle; | ||
depthStencilAttachment = null; | ||
get [Symbol.toStringTag]() { | ||
return 'WebGPUCanvasContext'; | ||
} | ||
constructor(device, adapter, props) { | ||
super(props); | ||
this.device = device; | ||
// TODO - hack to trigger resize? | ||
this.width = -1; | ||
this.height = -1; | ||
// Base class constructor cannot access derived methods/fields, so we need to call these functions in the subclass constructor | ||
this._setAutoCreatedCanvasId(`${this.device.id}-canvas`); | ||
// @ts-ignore TODO - we don't handle OffscreenRenderingContext. | ||
this.gpuCanvasContext = this.canvas.getContext('webgpu'); | ||
// TODO this has been replaced | ||
// this.format = this.gpuCanvasContext.getPreferredFormat(adapter); | ||
this.format = 'bgra8unorm'; | ||
this.updateSize([this.drawingBufferWidth, this.drawingBufferHeight]); | ||
} | ||
/** Destroy any textures produced while configured and remove the context configuration. */ | ||
destroy() { | ||
this.gpuCanvasContext.unconfigure(); | ||
this.handle.unconfigure(); | ||
} | ||
/** Update framebuffer with properly resized "swap chain" texture views */ | ||
getCurrentFramebuffer() { | ||
// Ensure the canvas context size is updated | ||
this.update(); | ||
getCurrentFramebuffer(options = { | ||
depthStencilFormat: 'depth24plus' | ||
}) { | ||
// Wrap the current canvas context texture in a luma.gl texture | ||
// const currentColorAttachment = this.device.createTexture({ | ||
// id: 'default-render-target', | ||
// handle: this.gpuCanvasContext.getCurrentTexture(), | ||
// format: this.format, | ||
// width: this.width, | ||
// height: this.height | ||
// }); | ||
// Wrap the current canvas context texture in a luma.gl texture | ||
const currentColorAttachment = this.getCurrentTexture(); | ||
this.width = currentColorAttachment.width; | ||
this.height = currentColorAttachment.height; | ||
// TODO - temporary debug code | ||
if (currentColorAttachment.width !== this.drawingBufferWidth || | ||
currentColorAttachment.height !== this.drawingBufferHeight) { | ||
const [oldWidth, oldHeight] = this.getDrawingBufferSize(); | ||
this.drawingBufferWidth = currentColorAttachment.width; | ||
this.drawingBufferHeight = currentColorAttachment.height; | ||
log.log(1, `${this}: Resized to compensate for initial canvas size mismatch ${oldWidth}x${oldHeight} => ${this.drawingBufferWidth}x${this.drawingBufferHeight}px`)(); | ||
} | ||
// Resize the depth stencil attachment | ||
this._createDepthStencilAttachment(); | ||
if (options?.depthStencilFormat) { | ||
this._createDepthStencilAttachment(options?.depthStencilFormat); | ||
} | ||
return new WebGPUFramebuffer(this.device, { | ||
@@ -62,33 +54,28 @@ colorAttachments: [currentColorAttachment], | ||
/** Resizes and updates render targets if necessary */ | ||
update() { | ||
const [width, height] = this.getPixelSize(); | ||
const sizeChanged = width !== this.width || height !== this.height; | ||
if (sizeChanged) { | ||
this.width = width; | ||
this.height = height; | ||
if (this.depthStencilAttachment) { | ||
this.depthStencilAttachment.destroy(); | ||
this.depthStencilAttachment = null; | ||
} | ||
// Reconfigure the canvas size. | ||
// https://www.w3.org/TR/webgpu/#canvas-configuration | ||
this.gpuCanvasContext.configure({ | ||
device: this.device.handle, | ||
format: getWebGPUTextureFormat(this.format), | ||
// Can be used to define e.g. -srgb views | ||
// viewFormats: [...] | ||
colorSpace: this.props.colorSpace, | ||
alphaMode: this.props.alphaMode | ||
}); | ||
log.log(1, `Resized to ${this.width}x${this.height}px`)(); | ||
updateSize(size) { | ||
if (this.depthStencilAttachment) { | ||
this.depthStencilAttachment.destroy(); | ||
this.depthStencilAttachment = null; | ||
} | ||
// Reconfigure the canvas size. | ||
// https://www.w3.org/TR/webgpu/#canvas-configuration | ||
this.handle.configure({ | ||
device: this.device.handle, | ||
format: this.device.preferredColorFormat, | ||
// Can be used to define e.g. -srgb views | ||
// viewFormats: [...] | ||
colorSpace: this.props.colorSpace, | ||
alphaMode: this.props.alphaMode | ||
}); | ||
} | ||
resize(options) { | ||
this.update(); | ||
if (!this.device.handle) | ||
return; | ||
if (this.props.autoResize) { | ||
return; | ||
} | ||
// Resize browser context . | ||
if (this.canvas) { | ||
const devicePixelRatio = this.getDevicePixelRatio(options?.useDevicePixels); | ||
this.setDevicePixelRatio(devicePixelRatio, options); | ||
this._setDevicePixelRatio(devicePixelRatio, options); | ||
return; | ||
@@ -101,15 +88,15 @@ } | ||
id: `${this.id}#color-texture`, | ||
handle: this.gpuCanvasContext.getCurrentTexture(), | ||
format: this.format | ||
handle: this.handle.getCurrentTexture(), | ||
format: this.device.preferredColorFormat | ||
}); | ||
} | ||
/** We build render targets on demand (i.e. not when size changes but when about to render) */ | ||
_createDepthStencilAttachment() { | ||
_createDepthStencilAttachment(depthStencilFormat) { | ||
if (!this.depthStencilAttachment) { | ||
this.depthStencilAttachment = this.device.createTexture({ | ||
id: `${this.id}#depth-stencil-texture`, | ||
format: this.depthStencilFormat, | ||
width: this.width, | ||
height: this.height, | ||
usage: GPUTextureUsage.RENDER_ATTACHMENT | ||
usage: Texture.RENDER_ATTACHMENT, | ||
format: depthStencilFormat, | ||
width: this.drawingBufferWidth, | ||
height: this.drawingBufferHeight | ||
}); | ||
@@ -120,1 +107,2 @@ } | ||
} | ||
//# sourceMappingURL=webgpu-canvas-context.js.map |
@@ -1,2 +0,2 @@ | ||
import type { DeviceInfo, DeviceLimits, CanvasContextProps, BufferProps, SamplerProps, ShaderProps, Texture, TextureProps, TextureFormat, ExternalTextureProps, FramebufferProps, RenderPipelineProps, ComputePipelineProps, RenderPassProps, ComputePassProps, VertexArrayProps, TransformFeedback, TransformFeedbackProps, QuerySet, QuerySetProps, DeviceProps } from '@luma.gl/core'; | ||
import type { DeviceInfo, DeviceLimits, DeviceTextureFormatCapabilities, CanvasContextProps, BufferProps, SamplerProps, ShaderProps, Texture, TextureProps, ExternalTextureProps, FramebufferProps, RenderPipelineProps, ComputePipelineProps, RenderPassProps, ComputePassProps, VertexArrayProps, TransformFeedback, TransformFeedbackProps, QuerySet, QuerySetProps, DeviceProps } from '@luma.gl/core'; | ||
import { Device, DeviceFeatures } from '@luma.gl/core'; | ||
@@ -17,8 +17,8 @@ import { WebGPUBuffer } from "./resources/webgpu-buffer.js"; | ||
export declare class WebGPUDevice extends Device { | ||
/** The underlying WebGPU device */ | ||
readonly handle: GPUDevice; | ||
/** type of this device */ | ||
readonly type = "webgpu"; | ||
/** The underlying WebGPU device */ | ||
readonly handle: GPUDevice; | ||
readonly adapter: GPUAdapter; | ||
readonly adapterInfo: GPUAdapterInfo; | ||
readonly preferredColorFormat: "rgba8unorm" | "bgra8unorm"; | ||
readonly preferredDepthFormat = "depth24plus"; | ||
readonly features: DeviceFeatures; | ||
@@ -31,13 +31,10 @@ readonly info: DeviceInfo; | ||
}>; | ||
renderPass: WebGPURenderPass | null; | ||
canvasContext: WebGPUCanvasContext | null; | ||
private _isLost; | ||
commandEncoder: GPUCommandEncoder | null; | ||
renderPass: WebGPURenderPass | null; | ||
readonly adapter: GPUAdapter; | ||
readonly adapterInfo: GPUAdapterInfo; | ||
constructor(props: DeviceProps, device: GPUDevice, adapter: GPUAdapter, adapterInfo: GPUAdapterInfo); | ||
destroy(): void; | ||
isTextureFormatSupported(format: TextureFormat): boolean; | ||
/** @todo implement proper check? */ | ||
isTextureFormatFilterable(format: TextureFormat): boolean; | ||
/** @todo implement proper check? */ | ||
isTextureFormatRenderable(format: TextureFormat): boolean; | ||
get isLost(): boolean; | ||
@@ -65,2 +62,3 @@ createBuffer(props: BufferProps | ArrayBuffer | ArrayBufferView): WebGPUBuffer; | ||
protected _getFeatures(): DeviceFeatures; | ||
_getDeviceSpecificTextureFormatCapabilities(capabilities: DeviceTextureFormatCapabilities): DeviceTextureFormatCapabilities; | ||
copyExternalImageToTexture(options: { | ||
@@ -67,0 +65,0 @@ texture: Texture; |
@@ -21,10 +21,8 @@ // luma.gl | ||
export class WebGPUDevice extends Device { | ||
/** The underlying WebGPU device */ | ||
handle; | ||
/** type of this device */ | ||
type = 'webgpu'; | ||
/** The underlying WebGPU device */ | ||
handle; | ||
/* The underlying WebGPU adapter */ | ||
adapter; | ||
/* The underlying WebGPU adapter's info */ | ||
adapterInfo; | ||
preferredColorFormat = navigator.gpu.getPreferredCanvasFormat(); | ||
preferredDepthFormat = 'depth24plus'; | ||
features; | ||
@@ -34,6 +32,11 @@ info; | ||
lost; | ||
canvasContext = null; | ||
renderPass = null; | ||
canvasContext; | ||
_isLost = false; | ||
// canvasContext: WebGPUCanvasContext | null = null; | ||
commandEncoder = null; | ||
renderPass = null; | ||
/* The underlying WebGPU adapter */ | ||
adapter; | ||
/* The underlying WebGPU adapter's info */ | ||
adapterInfo; | ||
constructor(props, device, adapter, adapterInfo) { | ||
@@ -50,4 +53,9 @@ super({ ...props, id: props.id || 'webgpu-device' }); | ||
// TODO is this the right way to make sure the error is an Error instance? | ||
const errorMessage = event instanceof GPUUncapturedErrorEvent ? event.error.message : 'Unknown error'; | ||
this.error(new Error(errorMessage)); | ||
const errorMessage = event instanceof GPUUncapturedErrorEvent ? event.error.message : 'Unknown WebGPU error'; | ||
this.reportError(new Error(errorMessage)); | ||
if (this.props.debug) { | ||
// eslint-disable-next-line no-debugger | ||
debugger; | ||
} | ||
event.preventDefault(); | ||
}); | ||
@@ -73,15 +81,2 @@ // "Context" loss handling | ||
} | ||
isTextureFormatSupported(format) { | ||
return !format.includes('webgl'); | ||
} | ||
/** @todo implement proper check? */ | ||
isTextureFormatFilterable(format) { | ||
return (this.isTextureFormatSupported(format) && | ||
!format.startsWith('depth') && | ||
!format.startsWith('stencil')); | ||
} | ||
/** @todo implement proper check? */ | ||
isTextureFormatRenderable(format) { | ||
return this.isTextureFormatSupported(format); | ||
} | ||
get isLost() { | ||
@@ -144,9 +139,13 @@ return this._isLost; | ||
submit() { | ||
// this.renderPass?.end(); | ||
const commandBuffer = this.commandEncoder?.finish(); | ||
if (commandBuffer) { | ||
this.handle.pushErrorScope('validation'); | ||
this.handle.queue.submit([commandBuffer]); | ||
this.handle.popErrorScope().then((error) => { | ||
if (error) { | ||
this.reportError(new Error(`WebGPU command submission failed: ${error.message}`)); | ||
} | ||
}); | ||
} | ||
this.commandEncoder = null; | ||
// this.renderPass = null; | ||
} | ||
@@ -205,2 +204,11 @@ // PRIVATE METHODS | ||
} | ||
_getDeviceSpecificTextureFormatCapabilities(capabilities) { | ||
const { format } = capabilities; | ||
if (format.includes('webgl')) { | ||
return { format, create: false, render: false, filter: false, blend: false, store: false }; | ||
} | ||
return capabilities; | ||
} | ||
// DEPRECATED METHODS | ||
// @deprecated | ||
copyExternalImageToTexture(options) { | ||
@@ -232,1 +240,2 @@ const { source, sourceX = 0, sourceY = 0, texture, mipLevel = 0, aspect = 'all', colorSpace = 'display-p3', premultipliedAlpha = false, | ||
} | ||
//# sourceMappingURL=webgpu-device.js.map |
@@ -446,6 +446,16 @@ (function webpackUniversalModuleDefinition(root, factory) { | ||
this.device = device; | ||
const isGLSL = props.source.includes("#version"); | ||
if (this.props.language === "glsl" || isGLSL) { | ||
throw new Error("GLSL shaders are not supported in WebGPU"); | ||
} | ||
this.device.handle.pushErrorScope("validation"); | ||
this.handle = this.props.handle || this.createHandle(); | ||
this.handle = this.props.handle || this.device.handle.createShaderModule({ code: props.source }); | ||
this.device.handle.popErrorScope().then((error) => { | ||
if (error) { | ||
import_core6.log.error(`${this} creation failed: | ||
"${error.message}"`, this, this.props.source)(); | ||
} | ||
}); | ||
this.handle.label = this.props.id; | ||
this._checkCompilationError(this.device.handle.popErrorScope()); | ||
this._checkCompilationError(); | ||
} | ||
@@ -455,9 +465,9 @@ get asyncCompilationStatus() { | ||
} | ||
async _checkCompilationError(errorScope) { | ||
const error = await errorScope; | ||
if (error) { | ||
this.debugShader(); | ||
const shaderLog = await this.getCompilationInfo(); | ||
import_core6.log.error(`Shader compilation error: ${error.message}`, shaderLog)(); | ||
throw new Error(`Shader compilation error: ${error.message}`); | ||
async _checkCompilationError() { | ||
const shaderLog = await this.getCompilationInfo(); | ||
const hasErrors = Boolean(shaderLog.find((msg) => msg.type === "error")); | ||
this.compilationStatus = hasErrors ? "error" : "success"; | ||
this.debugShader(); | ||
if (this.compilationStatus === "error") { | ||
import_core6.log.error(`Shader compilation error`, shaderLog)(); | ||
} | ||
@@ -473,11 +483,2 @@ } | ||
} | ||
// PRIVATE METHODS | ||
createHandle() { | ||
const { source } = this.props; | ||
const isGLSL = source.includes("#version"); | ||
if (this.props.language === "glsl" || isGLSL) { | ||
throw new Error("GLSL shaders are not supported in WebGPU"); | ||
} | ||
return this.device.handle.createShaderModule({ code: source }); | ||
} | ||
}; | ||
@@ -693,7 +694,7 @@ | ||
} | ||
function getShaderLayoutBinding(shaderLayout, bindingName) { | ||
function getShaderLayoutBinding(shaderLayout, bindingName, options) { | ||
const bindingLayout = shaderLayout.bindings.find( | ||
(binding) => binding.name === bindingName || `${binding.name}uniforms` === bindingName.toLocaleLowerCase() | ||
); | ||
if (!bindingLayout) { | ||
if (!bindingLayout && !options?.ignoreWarnings) { | ||
import_core7.log.warn(`Binding ${bindingName} not set: Not found in shader layout.`)(); | ||
@@ -706,10 +707,16 @@ } | ||
for (const [bindingName, value] of Object.entries(bindings)) { | ||
const bindingLayout = getShaderLayoutBinding(shaderLayout, bindingName); | ||
let bindingLayout = getShaderLayoutBinding(shaderLayout, bindingName); | ||
if (bindingLayout) { | ||
entries.push(getBindGroupEntry(value, bindingLayout.location)); | ||
} | ||
bindingLayout = getShaderLayoutBinding(shaderLayout, `${bindingName}Sampler`, { | ||
ignoreWarnings: true | ||
}); | ||
if (bindingLayout) { | ||
entries.push(getBindGroupEntry(value, bindingLayout.location, { sampler: true })); | ||
} | ||
} | ||
return entries; | ||
} | ||
function getBindGroupEntry(binding, index) { | ||
function getBindGroupEntry(binding, index, options) { | ||
if (binding instanceof import_core7.Buffer) { | ||
@@ -729,2 +736,8 @@ return { | ||
} else if (binding instanceof import_core7.Texture) { | ||
if (options?.sampler) { | ||
return { | ||
binding: index, | ||
resource: binding.sampler.handle | ||
}; | ||
} | ||
return { | ||
@@ -807,7 +820,7 @@ binding: index, | ||
if (!attribute) { | ||
import_core8.log.warn(`Unknown attribute ${name}`)(); | ||
import_core8.log.warn(`Supplied attribute not present in shader layout: ${name}`)(); | ||
return null; | ||
} | ||
if (attributeNames.has(name)) { | ||
throw new Error(`Duplicate attribute ${name}`); | ||
throw new Error(`Found multiple entries for attribute: ${name}`); | ||
} | ||
@@ -828,2 +841,5 @@ attributeNames.add(name); | ||
_bindGroup = null; | ||
get [Symbol.toStringTag]() { | ||
return "WebGPURenderPipeline"; | ||
} | ||
constructor(device, props) { | ||
@@ -838,3 +854,10 @@ super(device, props); | ||
import_core9.log.groupEnd(1)(); | ||
this.device.handle.pushErrorScope("validation"); | ||
this.handle = this.device.handle.createRenderPipeline(descriptor); | ||
this.device.handle.popErrorScope().then((error) => { | ||
if (error) { | ||
import_core9.log.error(`${this} creation failed: | ||
"${error.message}"`, this, this.props.vs?.source)(); | ||
} | ||
}); | ||
} | ||
@@ -854,2 +877,7 @@ this.handle.label = this.props.id; | ||
setBindings(bindings) { | ||
for (const [name, binding] of Object.entries(bindings)) { | ||
if (this._bindings[name] !== binding) { | ||
this._bindGroup = null; | ||
} | ||
} | ||
Object.assign(this._bindings, bindings); | ||
@@ -860,3 +888,10 @@ } | ||
const webgpuRenderPass = options.renderPass; | ||
this.device.handle.pushErrorScope("validation"); | ||
webgpuRenderPass.handle.setPipeline(this.handle); | ||
this.device.handle.popErrorScope().then((error) => { | ||
if (error) { | ||
import_core9.log.error(`${this} setPipeline failed: | ||
"${error.message}"`, this)(); | ||
} | ||
}); | ||
const bindGroup = this._getBindGroup(); | ||
@@ -904,11 +939,14 @@ if (bindGroup) { | ||
}; | ||
const targets = []; | ||
if (this.props.colorAttachmentFormats) { | ||
for (const format of this.props.colorAttachmentFormats) { | ||
targets.push(format ? { format: getWebGPUTextureFormat(format) } : null); | ||
} | ||
} else { | ||
targets.push({ format: getWebGPUTextureFormat(this.device.preferredColorFormat) }); | ||
} | ||
const fragment = { | ||
module: this.props.fs.handle, | ||
entryPoint: this.props.fragmentEntryPoint || "main", | ||
targets: [ | ||
{ | ||
// TODO exclamation mark hack! | ||
format: getWebGPUTextureFormat(this.device.getCanvasContext().format) | ||
} | ||
] | ||
targets | ||
}; | ||
@@ -923,2 +961,8 @@ const descriptor = { | ||
}; | ||
const depthFormat = this.props.depthStencilAttachmentFormat || this.device.preferredDepthFormat; | ||
if (this.props.parameters.depthWriteEnabled) { | ||
descriptor.depthStencil = { | ||
format: getWebGPUTextureFormat(depthFormat) | ||
}; | ||
} | ||
applyParametersToRenderPipelineDescriptor(descriptor, this.props.parameters); | ||
@@ -1010,3 +1054,10 @@ return descriptor; | ||
} | ||
this.device.handle.pushErrorScope("validation"); | ||
this.handle = this.props.handle || device.commandEncoder.beginRenderPass(renderPassDescriptor); | ||
this.device.handle.popErrorScope().then((error) => { | ||
if (error) { | ||
import_core12.log.error(`${this} creation failed: | ||
"${error.message}"`, this)(); | ||
} | ||
}); | ||
this.handle.label = this.props.id; | ||
@@ -1304,3 +1355,8 @@ import_core12.log.groupCollapsed(3, `new WebGPURenderPass(${this.id})`)(); | ||
if (webgpuIndexBuffer?.handle) { | ||
import_core14.log.warn("setting index buffer", webgpuIndexBuffer?.handle, webgpuIndexBuffer?.indexType)(); | ||
import_core14.log.info( | ||
3, | ||
"setting index buffer", | ||
webgpuIndexBuffer?.handle, | ||
webgpuIndexBuffer?.indexType | ||
)(); | ||
webgpuRenderPass.handle.setIndexBuffer( | ||
@@ -1315,3 +1371,3 @@ webgpuIndexBuffer?.handle, | ||
if (webgpuBuffer?.handle) { | ||
import_core14.log.warn(`setting vertex buffer ${location}`, webgpuBuffer?.handle)(); | ||
import_core14.log.info(3, `setting vertex buffer ${location}`, webgpuBuffer?.handle)(); | ||
webgpuRenderPass.handle.setVertexBuffer(location, webgpuBuffer?.handle); | ||
@@ -1337,28 +1393,34 @@ } | ||
device; | ||
gpuCanvasContext; | ||
/** Format of returned textures: "bgra8unorm", "rgba8unorm", "rgba16float". */ | ||
format = navigator.gpu.getPreferredCanvasFormat(); | ||
/** Default stencil format for depth textures */ | ||
depthStencilFormat = "depth24plus"; | ||
handle; | ||
depthStencilAttachment = null; | ||
get [Symbol.toStringTag]() { | ||
return "WebGPUCanvasContext"; | ||
} | ||
constructor(device, adapter, props) { | ||
super(props); | ||
this.device = device; | ||
this.width = -1; | ||
this.height = -1; | ||
this._setAutoCreatedCanvasId(`${this.device.id}-canvas`); | ||
this.gpuCanvasContext = this.canvas.getContext("webgpu"); | ||
this.format = "bgra8unorm"; | ||
this.updateSize([this.drawingBufferWidth, this.drawingBufferHeight]); | ||
} | ||
/** Destroy any textures produced while configured and remove the context configuration. */ | ||
destroy() { | ||
this.gpuCanvasContext.unconfigure(); | ||
this.handle.unconfigure(); | ||
} | ||
/** Update framebuffer with properly resized "swap chain" texture views */ | ||
getCurrentFramebuffer() { | ||
this.update(); | ||
getCurrentFramebuffer(options = { | ||
depthStencilFormat: "depth24plus" | ||
}) { | ||
const currentColorAttachment = this.getCurrentTexture(); | ||
this.width = currentColorAttachment.width; | ||
this.height = currentColorAttachment.height; | ||
this._createDepthStencilAttachment(); | ||
if (currentColorAttachment.width !== this.drawingBufferWidth || currentColorAttachment.height !== this.drawingBufferHeight) { | ||
const [oldWidth, oldHeight] = this.getDrawingBufferSize(); | ||
this.drawingBufferWidth = currentColorAttachment.width; | ||
this.drawingBufferHeight = currentColorAttachment.height; | ||
import_core15.log.log( | ||
1, | ||
`${this}: Resized to compensate for initial canvas size mismatch ${oldWidth}x${oldHeight} => ${this.drawingBufferWidth}x${this.drawingBufferHeight}px` | ||
)(); | ||
} | ||
if (options?.depthStencilFormat) { | ||
this._createDepthStencilAttachment(options?.depthStencilFormat); | ||
} | ||
return new WebGPUFramebuffer(this.device, { | ||
@@ -1370,30 +1432,25 @@ colorAttachments: [currentColorAttachment], | ||
/** Resizes and updates render targets if necessary */ | ||
update() { | ||
const [width, height] = this.getPixelSize(); | ||
const sizeChanged = width !== this.width || height !== this.height; | ||
if (sizeChanged) { | ||
this.width = width; | ||
this.height = height; | ||
if (this.depthStencilAttachment) { | ||
this.depthStencilAttachment.destroy(); | ||
this.depthStencilAttachment = null; | ||
} | ||
this.gpuCanvasContext.configure({ | ||
device: this.device.handle, | ||
format: getWebGPUTextureFormat(this.format), | ||
// Can be used to define e.g. -srgb views | ||
// viewFormats: [...] | ||
colorSpace: this.props.colorSpace, | ||
alphaMode: this.props.alphaMode | ||
}); | ||
import_core15.log.log(1, `Resized to ${this.width}x${this.height}px`)(); | ||
updateSize(size) { | ||
if (this.depthStencilAttachment) { | ||
this.depthStencilAttachment.destroy(); | ||
this.depthStencilAttachment = null; | ||
} | ||
this.handle.configure({ | ||
device: this.device.handle, | ||
format: this.device.preferredColorFormat, | ||
// Can be used to define e.g. -srgb views | ||
// viewFormats: [...] | ||
colorSpace: this.props.colorSpace, | ||
alphaMode: this.props.alphaMode | ||
}); | ||
} | ||
resize(options) { | ||
this.update(); | ||
if (!this.device.handle) | ||
return; | ||
if (this.props.autoResize) { | ||
return; | ||
} | ||
if (this.canvas) { | ||
const devicePixelRatio = this.getDevicePixelRatio(options?.useDevicePixels); | ||
this.setDevicePixelRatio(devicePixelRatio, options); | ||
this._setDevicePixelRatio(devicePixelRatio, options); | ||
return; | ||
@@ -1406,15 +1463,15 @@ } | ||
id: `${this.id}#color-texture`, | ||
handle: this.gpuCanvasContext.getCurrentTexture(), | ||
format: this.format | ||
handle: this.handle.getCurrentTexture(), | ||
format: this.device.preferredColorFormat | ||
}); | ||
} | ||
/** We build render targets on demand (i.e. not when size changes but when about to render) */ | ||
_createDepthStencilAttachment() { | ||
_createDepthStencilAttachment(depthStencilFormat) { | ||
if (!this.depthStencilAttachment) { | ||
this.depthStencilAttachment = this.device.createTexture({ | ||
id: `${this.id}#depth-stencil-texture`, | ||
format: this.depthStencilFormat, | ||
width: this.width, | ||
height: this.height, | ||
usage: GPUTextureUsage.RENDER_ATTACHMENT | ||
usage: import_core15.Texture.RENDER_ATTACHMENT, | ||
format: depthStencilFormat, | ||
width: this.drawingBufferWidth, | ||
height: this.drawingBufferHeight | ||
}); | ||
@@ -1448,10 +1505,8 @@ } | ||
var WebGPUDevice = class extends import_core17.Device { | ||
/** The underlying WebGPU device */ | ||
handle; | ||
/** type of this device */ | ||
type = "webgpu"; | ||
/** The underlying WebGPU device */ | ||
handle; | ||
/* The underlying WebGPU adapter */ | ||
adapter; | ||
/* The underlying WebGPU adapter's info */ | ||
adapterInfo; | ||
preferredColorFormat = navigator.gpu.getPreferredCanvasFormat(); | ||
preferredDepthFormat = "depth24plus"; | ||
features; | ||
@@ -1461,6 +1516,11 @@ info; | ||
lost; | ||
canvasContext = null; | ||
renderPass = null; | ||
canvasContext; | ||
_isLost = false; | ||
// canvasContext: WebGPUCanvasContext | null = null; | ||
commandEncoder = null; | ||
renderPass = null; | ||
/* The underlying WebGPU adapter */ | ||
adapter; | ||
/* The underlying WebGPU adapter's info */ | ||
adapterInfo; | ||
constructor(props, device, adapter, adapterInfo) { | ||
@@ -1475,4 +1535,8 @@ super({ ...props, id: props.id || "webgpu-device" }); | ||
device.addEventListener("uncapturederror", (event) => { | ||
const errorMessage = event instanceof GPUUncapturedErrorEvent ? event.error.message : "Unknown error"; | ||
this.error(new Error(errorMessage)); | ||
const errorMessage = event instanceof GPUUncapturedErrorEvent ? event.error.message : "Unknown WebGPU error"; | ||
this.reportError(new Error(errorMessage)); | ||
if (this.props.debug) { | ||
debugger; | ||
} | ||
event.preventDefault(); | ||
}); | ||
@@ -1496,13 +1560,2 @@ this.lost = new Promise(async (resolve) => { | ||
} | ||
isTextureFormatSupported(format) { | ||
return !format.includes("webgl"); | ||
} | ||
/** @todo implement proper check? */ | ||
isTextureFormatFilterable(format) { | ||
return this.isTextureFormatSupported(format) && !format.startsWith("depth") && !format.startsWith("stencil"); | ||
} | ||
/** @todo implement proper check? */ | ||
isTextureFormatRenderable(format) { | ||
return this.isTextureFormatSupported(format); | ||
} | ||
get isLost() { | ||
@@ -1567,3 +1620,9 @@ return this._isLost; | ||
if (commandBuffer) { | ||
this.handle.pushErrorScope("validation"); | ||
this.handle.queue.submit([commandBuffer]); | ||
this.handle.popErrorScope().then((error) => { | ||
if (error) { | ||
this.reportError(new Error(`WebGPU command submission failed: ${error.message}`)); | ||
} | ||
}); | ||
} | ||
@@ -1618,2 +1677,11 @@ this.commandEncoder = null; | ||
} | ||
_getDeviceSpecificTextureFormatCapabilities(capabilities) { | ||
const { format } = capabilities; | ||
if (format.includes("webgl")) { | ||
return { format, create: false, render: false, filter: false, blend: false, store: false }; | ||
} | ||
return capabilities; | ||
} | ||
// DEPRECATED METHODS | ||
// @deprecated | ||
copyExternalImageToTexture(options) { | ||
@@ -1620,0 +1688,0 @@ const { |
@@ -7,4 +7,8 @@ (function webpackUniversalModuleDefinition(root, factory) { | ||
else root['luma'] = factory();})(globalThis, function () { | ||
var __exports__=(()=>{var ye=Object.create;var F=Object.defineProperty;var ve=Object.getOwnPropertyDescriptor;var Se=Object.getOwnPropertyNames;var Ce=Object.getPrototypeOf,Pe=Object.prototype.hasOwnProperty;var Ee=(n,e)=>()=>(e||n((e={exports:{}}).exports,e),e.exports),Be=(n,e)=>{for(var t in e)F(n,t,{get:e[t],enumerable:!0})},I=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Se(e))!Pe.call(n,i)&&i!==t&&F(n,i,{get:()=>e[i],enumerable:!(r=ve(e,i))||r.enumerable});return n},R=(n,e,t)=>(I(n,e,"default"),t&&I(t,e,"default")),h=(n,e,t)=>(t=n!=null?ye(Ce(n)):{},I(e||!n||!n.__esModule?F(t,"default",{value:n,enumerable:!0}):t,n)),Ae=n=>I(F({},"__esModule",{value:!0}),n);var o=Ee((We,te)=>{te.exports=globalThis.luma});var D={};Be(D,{WebGPUBuffer:()=>b,WebGPUDevice:()=>v,WebGPUSampler:()=>p,WebGPUShader:()=>P,WebGPUTexture:()=>C,webgpuAdapter:()=>fe});R(D,h(o(),1));var f=h(o(),1);var X=h(o(),1);var O=h(o(),1);function Te(n){return n.byteLength||n.data?.byteLength||0}var b=class extends O.Buffer{device;handle;byteLength;constructor(e,t){super(e,t),this.device=e,this.byteLength=Te(t);let r=Boolean(t.data),i=Math.ceil(this.byteLength/4)*4;this.handle=this.props.handle||this.device.handle.createBuffer({size:i,usage:this.props.usage||GPUBufferUsage.VERTEX|GPUBufferUsage.COPY_DST,mappedAtCreation:this.props.mappedAtCreation||r,label:this.props.id}),t.data&&this._writeMapped(t.data),r&&!t.mappedAtCreation&&this.handle.unmap()}destroy(){this.handle?.destroy(),this.handle=null}write(e,t=0){this.device.handle.queue.writeBuffer(this.handle,t,e.buffer,e.byteOffset,e.byteLength)}async readAsync(e=0,t=this.byteLength){let r=new b(this.device,{usage:O.Buffer.MAP_READ|O.Buffer.COPY_DST,byteLength:t}),i=this.device.handle.createCommandEncoder();i.copyBufferToBuffer(this.handle,e,r.handle,0,t),this.device.handle.queue.submit([i.finish()]),await r.handle.mapAsync(GPUMapMode.READ,e,t);let s=r.handle.getMappedRange().slice(0);return r.handle.unmap(),r.destroy(),new Uint8Array(s)}_writeMapped(e){let t=this.handle.getMappedRange();new e.constructor(t).set(e)}mapAsync(e,t=0,r){return this.handle.mapAsync(e,t,r)}getMappedRange(e=0,t){return this.handle.getMappedRange(e,t)}unmap(){this.handle.unmap()}};var w=h(o(),1);function S(n){if(n.includes("webgl"))throw new Error("webgl-only format");return n}var re=h(o(),1),p=class extends re.Sampler{device;handle;constructor(e,t){super(e,t),this.device=e;let r={...this.props,mipmapFilter:void 0};t.type!=="comparison-sampler"&&delete r.compare,t.mipmapFilter&&t.mipmapFilter!=="none"&&(r.mipmapFilter=t.mipmapFilter),this.handle=this.handle||this.device.handle.createSampler(r),this.handle.label=this.props.id}destroy(){this.handle=null}};var ne=h(o(),1),G=class extends ne.TextureView{device;handle;texture;constructor(e,t){super(e,t),this.device=e,this.texture=t.texture,this.handle=this.handle||this.texture.handle.createView({format:t.format||this.texture.format,dimension:t.dimension||this.texture.dimension,aspect:t.aspect,baseMipLevel:t.baseMipLevel,mipLevelCount:t.mipLevelCount,baseArrayLayer:t.baseArrayLayer,arrayLayerCount:t.arrayLayerCount}),this.handle.label=this.props.id}destroy(){this.handle=null}};var Ge={"1d":"1d","2d":"2d","2d-array":"2d",cube:"2d","cube-array":"2d","3d":"3d"},C=class extends w.Texture{device;handle;sampler;view;constructor(e,t){super(e,t),this.device=e;let r={...this.props};t.data&&(r.data=t.data),this.initialize(r)}destroy(){this.handle?.destroy(),this.handle=null}createView(e){return new G(this.device,{...e,texture:this})}initialize(e){this.handle=this.props.handle||this.createHandle(),this.handle.label||=this.id,this.props.data&&(w.Texture.isExternalImage(this.props.data)?this.copyExternalImage({image:this.props.data}):this.setData({data:this.props.data})),this.width=this.handle.width,this.height=this.handle.height,this.sampler=e.sampler instanceof p?e.sampler:new p(this.device,e.sampler||{}),this.view=new G(this.device,{...this.props,texture:this})}createHandle(){let e=this.props.width||this.props.data?.width||1,t=this.props.height||this.props.data?.height||1;return this.device.handle.createTexture({label:this.id,size:{width:e,height:t,depthOrArrayLayers:this.depth},usage:this.props.usage||w.Texture.TEXTURE|w.Texture.COPY_DST,dimension:Ge[this.dimension],format:S(this.format),mipLevelCount:this.mipLevels,sampleCount:this.props.samples})}createGPUTextureView(){return this.handle.createView({label:this.id})}setSampler(e){return this.sampler=e instanceof p?e:new p(this.device,e),this}setTexture1DData(e){throw new Error("not implemented")}setTexture2DData(e,t,r){throw new Error("not implemented")}setTexture3DData(e,t,r){throw new Error("not implemented")}setTextureCubeData(e,t){throw new Error("not implemented")}setTextureArrayData(e){throw new Error("not implemented")}setTextureCubeArrayData(e){throw new Error("not implemented")}setData(e){if(ArrayBuffer.isView(e.data)){let t=new Uint8ClampedArray(e.data.buffer),r=new ImageData(t,this.width,this.height);return this.copyExternalImage({image:r})}throw new Error("Texture.setData: Use CommandEncoder to upload data to texture in WebGPU")}copyExternalImage(e){let t=w.Texture.getExternalImageSize(e.image),r={...w.Texture.defaultCopyExternalImageOptions,...t,...e},{image:i,sourceX:s,sourceY:a,width:l,height:u,depth:d,mipLevel:g,x:c,y:T,z:J,aspect:ge,colorSpace:be,premultipliedAlpha:xe,flipY:we}=r;return this.device.handle.queue.copyExternalImageToTexture({source:i,origin:[s,a],flipY:we},{texture:this.handle,origin:[c,T,J],mipLevel:g,aspect:ge,colorSpace:be,premultipliedAlpha:xe},[l,u,d]),{width:l,height:u}}};var ie=h(o(),1);var W=class extends ie.ExternalTexture{device;handle;sampler;constructor(e,t){super(e,t),this.device=e,this.handle=this.props.handle||this.device.handle.importExternalTexture({source:t.source,colorSpace:t.colorSpace}),this.sampler=null}destroy(){this.handle=null}setSampler(e){return this.sampler=e instanceof p?e:new p(this.device,e),this}};var M=h(o(),1),P=class extends M.Shader{device;handle;constructor(e,t){super(e,t),this.device=e,this.device.handle.pushErrorScope("validation"),this.handle=this.props.handle||this.createHandle(),this.handle.label=this.props.id,this._checkCompilationError(this.device.handle.popErrorScope())}get asyncCompilationStatus(){return this.getCompilationInfo().then(()=>this.compilationStatus)}async _checkCompilationError(e){let t=await e;if(t){this.debugShader();let r=await this.getCompilationInfo();throw M.log.error(`Shader compilation error: ${t.message}`,r)(),new Error(`Shader compilation error: ${t.message}`)}}destroy(){this.handle=null}async getCompilationInfo(){return(await this.handle.getCompilationInfo()).messages}createHandle(){let{source:e}=this.props,t=e.includes("#version");if(this.props.language==="glsl"||t)throw new Error("GLSL shaders are not supported in WebGPU");return this.device.handle.createShaderModule({code:e})}};var B=h(o(),1);function m(n){return n.depthStencil=n.depthStencil||{format:"depth24plus",stencilFront:{},stencilBack:{},depthWriteEnabled:!1,depthCompare:"less-equal"},n.depthStencil}function U(n){return m(n).stencilFront}function k(n){return m(n).stencilBack}var Le={cullMode:(n,e,t)=>{t.primitive=t.primitive||{},t.primitive.cullMode=e},frontFace:(n,e,t)=>{t.primitive=t.primitive||{},t.primitive.frontFace=e},depthWriteEnabled:(n,e,t)=>{let r=m(t);r.depthWriteEnabled=e},depthCompare:(n,e,t)=>{let r=m(t);r.depthCompare=e},depthFormat:(n,e,t)=>{let r=m(t);r.format=e},depthBias:(n,e,t)=>{let r=m(t);r.depthBias=e},depthBiasSlopeScale:(n,e,t)=>{let r=m(t);r.depthBiasSlopeScale=e},depthBiasClamp:(n,e,t)=>{let r=m(t);r.depthBiasClamp=e},stencilReadMask:(n,e,t)=>{let r=m(t);r.stencilReadMask=e},stencilWriteMask:(n,e,t)=>{let r=m(t);r.stencilWriteMask=e},stencilCompare:(n,e,t)=>{let r=U(t),i=k(t);r.compare=e,i.compare=e},stencilPassOperation:(n,e,t)=>{let r=U(t),i=k(t);r.passOp=e,i.passOp=e},stencilFailOperation:(n,e,t)=>{let r=U(t),i=k(t);r.failOp=e,i.failOp=e},stencilDepthFailOperation:(n,e,t)=>{let r=U(t),i=k(t);r.depthFailOp=e,i.depthFailOp=e},sampleCount:(n,e,t)=>{t.multisample=t.multisample||{},t.multisample.count=e},sampleMask:(n,e,t)=>{t.multisample=t.multisample||{},t.multisample.mask=e},sampleAlphaToCoverageEnabled:(n,e,t)=>{t.multisample=t.multisample||{},t.multisample.alphaToCoverageEnabled=e},colorMask:(n,e,t)=>{let r=se(t);r[0].writeMask=e},blendColorOperation:(n,e,t)=>{se(t)}},_e={primitive:{cullMode:"back",topology:"triangle-list"},vertex:{module:void 0,entryPoint:"main"},fragment:{module:void 0,entryPoint:"main",targets:[]},layout:"auto"};function ae(n,e={}){Object.assign(n,{..._e,...n}),De(n,e)}function De(n,e){for(let[t,r]of Object.entries(e)){let i=Le[t];if(!i)throw new Error(`Illegal parameter ${t}`);i(t,r,n)}}function se(n){if(n.fragment.targets=n.fragment?.targets||[],!Array.isArray(n.fragment?.targets))throw new Error("colorstate");return n.fragment?.targets?.length===0&&n.fragment.targets?.push({}),n.fragment?.targets}var x=h(o(),1);function V(n,e,t,r){let i=Fe(r,t);return n.createBindGroup({layout:e,entries:i})}function Ie(n,e){let t=n.bindings.find(r=>r.name===e||`${r.name}uniforms`===e.toLocaleLowerCase());return t||x.log.warn(`Binding ${e} not set: Not found in shader layout.`)(),t||null}function Fe(n,e){let t=[];for(let[r,i]of Object.entries(n)){let s=Ie(e,r);s&&t.push(Re(i,s.location))}return t}function Re(n,e){if(n instanceof x.Buffer)return{binding:e,resource:{buffer:n.handle}};if(n instanceof x.Sampler)return{binding:e,resource:n.handle};if(n instanceof x.Texture)return{binding:e,resource:n.handle.createView({label:"bind-group-auto-created"})};throw new Error("invalid binding")}var E=h(o(),1);function Z(n){if(n.endsWith("-webgl"))throw new Error(`WebGPU does not support vertex format ${n}`);return n}function he(n,e){let t=[],r=new Set;for(let i of e){let s=[],a="vertex",l=0,u=i.format;if(i.attributes)for(let d of i.attributes){let g=d.attribute,c=oe(n,g,r),T=c?.location;a=c?.stepMode||(c?.name.startsWith("instance")?"instance":"vertex"),s.push({format:Z(d.format||i.format),offset:d.byteOffset,shaderLocation:T}),l+=(0,E.decodeVertexFormat)(u).byteLength}else{let d=oe(n,i.name,r);if(!d)continue;l=(0,E.decodeVertexFormat)(u).byteLength,a=d.stepMode||(d.name.startsWith("instance")?"instance":"vertex"),s.push({format:Z(u),offset:0,shaderLocation:d.location})}t.push({arrayStride:i.byteStride||l,stepMode:a,attributes:s})}for(let i of n.attributes)r.has(i.name)||t.push({arrayStride:(0,E.decodeVertexFormat)("float32x3").byteLength,stepMode:i.stepMode||(i.name.startsWith("instance")?"instance":"vertex"),attributes:[{format:Z("float32x3"),offset:0,shaderLocation:i.location}]});return t}function oe(n,e,t){let r=n.attributes.find(i=>i.name===e);if(!r)return E.log.warn(`Unknown attribute ${e}`)(),null;if(t.has(e))throw new Error(`Duplicate attribute ${e}`);return t.add(e),r}var q=class extends B.RenderPipeline{device;handle;vs;fs=null;_bindings;_bindGroupLayout=null;_bindGroup=null;constructor(e,t){if(super(e,t),this.device=e,this.handle=this.props.handle,!this.handle){let r=this._getRenderPipelineDescriptor();B.log.groupCollapsed(1,`new WebGPURenderPipeline(${this.id})`)(),B.log.probe(1,JSON.stringify(r,null,2))(),B.log.groupEnd(1)(),this.handle=this.device.handle.createRenderPipeline(r)}this.handle.label=this.props.id,this.vs=t.vs,this.fs=t.fs,this._bindings={...this.props.bindings}}destroy(){this.handle=null}setBindings(e){Object.assign(this._bindings,e)}draw(e){let t=e.renderPass;t.handle.setPipeline(this.handle);let r=this._getBindGroup();return r&&t.handle.setBindGroup(0,r),e.vertexArray.bindBeforeRender(e.renderPass),e.indexCount?t.handle.drawIndexed(e.indexCount,e.instanceCount,e.firstIndex,e.baseVertex,e.firstInstance):t.handle.draw(e.vertexCount||0,e.instanceCount||1,e.firstInstance),e.vertexArray.unbindAfterRender(e.renderPass),!0}_getBindGroup(){return this.shaderLayout.bindings.length===0?null:(this._bindGroupLayout=this._bindGroupLayout||this.handle.getBindGroupLayout(0),this._bindGroup=this._bindGroup||V(this.device.handle,this._bindGroupLayout,this.shaderLayout,this._bindings),this._bindGroup)}_getRenderPipelineDescriptor(){let e={module:this.props.vs.handle,entryPoint:this.props.vertexEntryPoint||"main",buffers:he(this.shaderLayout,this.props.bufferLayout)},t={module:this.props.fs.handle,entryPoint:this.props.fragmentEntryPoint||"main",targets:[{format:S(this.device.getCanvasContext().format)}]},r={vertex:e,fragment:t,primitive:{topology:this.props.topology},layout:"auto"};return ae(r,this.props.parameters),r}};var de=h(o(),1),A=class extends de.Framebuffer{device;colorAttachments=[];depthStencilAttachment=null;constructor(e,t){super(e,t),this.device=e,this.autoCreateAttachmentTextures()}updateAttachments(){}};var le=h(o(),1);var z=class extends le.ComputePipeline{device;handle;_bindGroupLayout=null;_bindGroup=null;_bindings={};constructor(e,t){super(e,t),this.device=e;let r=this.props.shader;this.handle=this.props.handle||this.device.handle.createComputePipeline({label:this.props.id,compute:{module:r.handle,entryPoint:this.props.entryPoint,constants:this.props.constants},layout:"auto"})}setBindings(e){Object.assign(this._bindings,e)}_getBindGroup(){return this._bindGroupLayout=this._bindGroupLayout||this.handle.getBindGroupLayout(0),this._bindGroup=this._bindGroup||V(this.device.handle,this._bindGroupLayout,this.shaderLayout,this._bindings),this._bindGroup}};var y=h(o(),1),$=class extends y.RenderPass{device;handle;pipeline=null;constructor(e,t={}){super(e,t),this.device=e;let r=t.framebuffer||e.getCanvasContext().getCurrentFramebuffer(),i=this.getRenderPassDescriptor(r),s=t.timestampQuerySet;if(s&&(i.occlusionQuerySet=s.handle),e.features.has("timestamp-query")){let a=t.timestampQuerySet;i.timestampWrites=a?{querySet:a.handle,beginningOfPassWriteIndex:t.beginTimestampIndex,endOfPassWriteIndex:t.endTimestampIndex}:void 0}if(!e.commandEncoder)throw new Error("commandEncoder not available");this.handle=this.props.handle||e.commandEncoder.beginRenderPass(i),this.handle.label=this.props.id,y.log.groupCollapsed(3,`new WebGPURenderPass(${this.id})`)(),y.log.probe(3,JSON.stringify(i,null,2))(),y.log.groupEnd(3)()}destroy(){}end(){this.handle.end()}setPipeline(e){this.pipeline=e,this.handle.setPipeline(this.pipeline.handle)}setBindings(e){this.pipeline?.setBindings(e);let t=this.pipeline?._getBindGroup();t&&this.handle.setBindGroup(0,t)}setIndexBuffer(e,t,r=0,i){this.handle.setIndexBuffer(e.handle,t,r,i)}setVertexBuffer(e,t,r=0){this.handle.setVertexBuffer(e,t.handle,r)}draw(e){e.indexCount?this.handle.drawIndexed(e.indexCount,e.instanceCount,e.firstIndex,e.baseVertex,e.firstInstance):this.handle.draw(e.vertexCount||0,e.instanceCount||1,e.firstIndex,e.firstInstance)}drawIndirect(){}setParameters(e){let{blendConstant:t,stencilReference:r,scissorRect:i,viewport:s}=e;t&&this.handle.setBlendConstant(t),r&&this.handle.setStencilReference(r),i&&this.handle.setScissorRect(i[0],i[1],i[2],i[3]),s&&this.handle.setViewport(s[0],s[1],s[2],s[3],s[4],s[5])}pushDebugGroup(e){this.handle.pushDebugGroup(e)}popDebugGroup(){this.handle.popDebugGroup()}insertDebugMarker(e){this.handle.insertDebugMarker(e)}beginOcclusionQuery(e){this.handle.beginOcclusionQuery(e)}endOcclusionQuery(){this.handle.endOcclusionQuery()}getRenderPassDescriptor(e){let t={colorAttachments:[]};if(t.colorAttachments=e.colorAttachments.map((r,i)=>({loadOp:this.props.clearColor!==!1?"clear":"load",colorClearValue:this.props.clearColors?.[i]||this.props.clearColor||y.RenderPass.defaultClearColor,storeOp:this.props.discard?"discard":"store",view:r.handle})),e.depthStencilAttachment){t.depthStencilAttachment={view:e.depthStencilAttachment.handle};let{depthStencilAttachment:r}=t;this.props.depthReadOnly&&(r.depthReadOnly=!0),this.props.clearDepth!==!1&&(r.depthClearValue=this.props.clearDepth),!0&&(r.depthLoadOp=this.props.clearDepth!==!1?"clear":"load",r.depthStoreOp="store"),!1&&(r.stencilLoadOp=this.props.clearStencil!==!1?"clear":"load",r.stencilStoreOp="store")}return t}};var pe=h(o(),1),Q=class extends pe.ComputePass{device;handle;_webgpuPipeline=null;constructor(e,t){super(e,t),this.device=e;let r;if(e.features.has("timestamp-query")){let i=t.timestampQuerySet;i&&(r={querySet:i.handle,beginningOfPassWriteIndex:t.beginTimestampIndex,endOfPassWriteIndex:t.endTimestampIndex})}this.handle=this.props.handle||e.commandEncoder?.beginComputePass({label:this.props.id,timestampWrites:r})}destroy(){}end(){this.handle.end()}setPipeline(e){let t=e;this.handle.setPipeline(t.handle),this._webgpuPipeline=t,this.setBindings([])}setBindings(e){let t=this._webgpuPipeline._getBindGroup();this.handle.setBindGroup(0,t)}dispatch(e,t,r){this.handle.dispatchWorkgroups(e,t,r)}dispatchIndirect(e,t=0){let r=e;this.handle.dispatchWorkgroupsIndirect(r.handle,t)}pushDebugGroup(e){this.handle.pushDebugGroup(e)}popDebugGroup(){this.handle.popDebugGroup()}insertDebugMarker(e){this.handle.insertDebugMarker(e)}};var L=h(o(),1);var ht=globalThis.document||{},dt=globalThis.process||{},lt=globalThis.console,ue=globalThis.navigator||{};function N(n){if(typeof window<"u"&&window.process?.type==="renderer"||typeof process<"u"&&Boolean(process.versions?.electron))return!0;let e=typeof navigator<"u"&&navigator.userAgent,t=n||e;return Boolean(t&&t.indexOf("Electron")>=0)}function ce(){return!(typeof process=="object"&&String(process)==="[object process]"&&!process?.browser)||N()}function K(n){return!n&&!ce()?"Node":N(n)?"Electron":(n||ue.userAgent||"").indexOf("Edge")>-1?"Edge":globalThis.chrome?"Chrome":globalThis.safari?"Safari":globalThis.mozInnerScreenX?"Firefox":"Unknown"}var j=class extends L.VertexArray{get[Symbol.toStringTag](){return"WebGPUVertexArray"}device;handle;constructor(e,t){super(e,t),this.device=e}destroy(){}setIndexBuffer(e){this.indexBuffer=e}setBuffer(e,t){this.attributes[e]=t}bindBeforeRender(e,t,r){let i=e,s=this.indexBuffer;s?.handle&&(L.log.warn("setting index buffer",s?.handle,s?.indexType)(),i.handle.setIndexBuffer(s?.handle,s?.indexType));for(let a=0;a<this.maxVertexAttributes;a++){let l=this.attributes[a];l?.handle&&(L.log.warn(`setting vertex buffer ${a}`,l?.handle)(),i.handle.setVertexBuffer(a,l?.handle))}}unbindAfterRender(e){}static isConstantAttributeZeroSupported(e){return K()==="Chrome"}};var Y=h(o(),1);var _=class extends Y.CanvasContext{device;gpuCanvasContext;format=navigator.gpu.getPreferredCanvasFormat();depthStencilFormat="depth24plus";depthStencilAttachment=null;constructor(e,t,r){super(r),this.device=e,this.width=-1,this.height=-1,this._setAutoCreatedCanvasId(`${this.device.id}-canvas`),this.gpuCanvasContext=this.canvas.getContext("webgpu"),this.format="bgra8unorm"}destroy(){this.gpuCanvasContext.unconfigure()}getCurrentFramebuffer(){this.update();let e=this.getCurrentTexture();return this.width=e.width,this.height=e.height,this._createDepthStencilAttachment(),new A(this.device,{colorAttachments:[e],depthStencilAttachment:this.depthStencilAttachment})}update(){let[e,t]=this.getPixelSize();(e!==this.width||t!==this.height)&&(this.width=e,this.height=t,this.depthStencilAttachment&&(this.depthStencilAttachment.destroy(),this.depthStencilAttachment=null),this.gpuCanvasContext.configure({device:this.device.handle,format:S(this.format),colorSpace:this.props.colorSpace,alphaMode:this.props.alphaMode}),Y.log.log(1,`Resized to ${this.width}x${this.height}px`)())}resize(e){if(this.update(),!!this.device.handle&&this.canvas){let t=this.getDevicePixelRatio(e?.useDevicePixels);this.setDevicePixelRatio(t,e);return}}getCurrentTexture(){return this.device.createTexture({id:`${this.id}#color-texture`,handle:this.gpuCanvasContext.getCurrentTexture(),format:this.format})}_createDepthStencilAttachment(){return this.depthStencilAttachment||(this.depthStencilAttachment=this.device.createTexture({id:`${this.id}#depth-stencil-texture`,format:this.depthStencilFormat,width:this.width,height:this.height,usage:GPUTextureUsage.RENDER_ATTACHMENT})),this.depthStencilAttachment}};var me=h(o(),1),H=class extends me.QuerySet{device;handle;constructor(e,t){super(e,t),this.device=e,this.handle=this.props.handle||this.device.handle.createQuerySet({type:this.props.type,count:this.props.count}),this.handle.label=this.props.id}destroy(){this.handle?.destroy(),this.handle=null}};var v=class extends X.Device{type="webgpu";handle;adapter;adapterInfo;features;info;limits;lost;canvasContext=null;_isLost=!1;commandEncoder=null;renderPass=null;constructor(e,t,r,i){if(super({...e,id:e.id||"webgpu-device"}),this.handle=t,this.adapter=r,this.adapterInfo=i,this.info=this._getInfo(),this.features=this._getFeatures(),this.limits=this.handle.limits,t.addEventListener("uncapturederror",s=>{let a=s instanceof GPUUncapturedErrorEvent?s.error.message:"Unknown error";this.error(new Error(a))}),this.lost=new Promise(async s=>{let a=await this.handle.lost;this._isLost=!0,s({reason:"destroyed",message:a.message})}),e.createCanvasContext){let s=e.createCanvasContext===!0?{}:e.createCanvasContext;this.canvasContext=new _(this,this.adapter,s)}}destroy(){this.handle.destroy()}isTextureFormatSupported(e){return!e.includes("webgl")}isTextureFormatFilterable(e){return this.isTextureFormatSupported(e)&&!e.startsWith("depth")&&!e.startsWith("stencil")}isTextureFormatRenderable(e){return this.isTextureFormatSupported(e)}get isLost(){return this._isLost}createBuffer(e){let t=this._normalizeBufferProps(e);return new b(this,t)}createTexture(e){return new C(this,e)}createExternalTexture(e){return new W(this,e)}createShader(e){return new P(this,e)}createSampler(e){return new p(this,e)}createRenderPipeline(e){return new q(this,e)}createFramebuffer(e){return new A(this,e)}createComputePipeline(e){return new z(this,e)}createVertexArray(e){return new j(this,e)}beginRenderPass(e){return this.commandEncoder=this.commandEncoder||this.handle.createCommandEncoder(),new $(this,e)}beginComputePass(e){return this.commandEncoder=this.commandEncoder||this.handle.createCommandEncoder(),new Q(this,e)}createTransformFeedback(e){throw new Error("Transform feedback not supported in WebGPU")}createQuerySet(e){return new H(this,e)}createCanvasContext(e){return new _(this,this.adapter,e)}submit(){let e=this.commandEncoder?.finish();e&&this.handle.queue.submit([e]),this.commandEncoder=null}_getInfo(){let[e,t]=(this.adapterInfo.driver||"").split(" Version "),r=this.adapterInfo.vendor||this.adapter.__brand||"unknown",i=e||"",s=t||"",a=r==="apple"?"apple":"unknown",l=this.adapterInfo.architecture||"unknown",u=this.adapterInfo.backend||"unknown",d=(this.adapterInfo.type||"").split(" ")[0].toLowerCase()||"unknown";return{type:"webgpu",vendor:r,renderer:i,version:s,gpu:a,gpuType:d,gpuBackend:u,gpuArchitecture:l,shadingLanguage:"wgsl",shadingLanguageVersion:100}}_getFeatures(){let e=new Set(this.handle.features);e.has("depth-clamping")&&(e.delete("depth-clamping"),e.add("depth-clip-control")),e.has("texture-compression-bc")&&e.add("texture-compression-bc5-webgl");let t=["timer-query-webgl","compilation-status-async-webgl","float32-renderable-webgl","float16-renderable-webgl","norm16-renderable-webgl","texture-filterable-anisotropic-webgl","shader-noperspective-interpolation-webgl"];for(let r of t)e.add(r);return new X.DeviceFeatures(Array.from(e),this.props._disabledFeatures)}copyExternalImageToTexture(e){let{source:t,sourceX:r=0,sourceY:i=0,texture:s,mipLevel:a=0,aspect:l="all",colorSpace:u="display-p3",premultipliedAlpha:d=!1,width:g=s.width,height:c=s.height,depth:T=1}=e,J=s;this.handle?.queue.copyExternalImageToTexture({source:t,origin:[r,i]},{texture:J.handle,origin:[0,0,0],mipLevel:a,aspect:l,colorSpace:u,premultipliedAlpha:d},[g,c,T])}};var ee=class extends f.Adapter{type="webgpu";constructor(){super(),v.adapter=this}isSupported(){return Boolean(typeof navigator<"u"&&navigator.gpu)}async create(e){if(!navigator.gpu)throw new Error("WebGPU not available. Open in Chrome Canary and turn on chrome://flags/#enable-unsafe-webgpu");f.log.groupCollapsed(1,"WebGPUDevice created")();let t=await navigator.gpu.requestAdapter({powerPreference:"high-performance"});if(!t)throw new Error("Failed to request WebGPU adapter");let r=await t.requestAdapterInfo();f.log.probe(2,"Adapter available",r)();let i=[],s={};if(e._requestMaxLimits){i.push(...Array.from(t.features));let u=Object.keys(t.limits).filter(d=>!["minSubgroupSize","maxSubgroupSize"].includes(d));for(let d of u){let g=d,c=t.limits[g];typeof c=="number"&&(s[g]=c)}}let a=await t.requestDevice({requiredFeatures:i,requiredLimits:s});f.log.probe(1,"GPUDevice available")();let l=new v(e,a,t,r);return f.log.probe(1,"Device created. For more info, set chrome://flags/#enable-webgpu-developer-features")(),f.log.table(1,l.info)(),f.log.groupEnd(1)(),l}async attach(e){throw new Error("WebGPUAdapter.attach() not implemented")}},fe=new ee;return Ae(D);})(); | ||
var __exports__=(()=>{var Se=Object.create;var R=Object.defineProperty;var Ee=Object.getOwnPropertyDescriptor;var Ce=Object.getOwnPropertyNames;var Pe=Object.getPrototypeOf,Be=Object.prototype.hasOwnProperty;var Ae=(n,e)=>()=>(e||n((e={exports:{}}).exports,e),e.exports),Ge=(n,e)=>{for(var t in e)R(n,t,{get:e[t],enumerable:!0})},W=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Ce(e))!Be.call(n,i)&&i!==t&&R(n,i,{get:()=>e[i],enumerable:!(r=Ee(e,i))||r.enumerable});return n},O=(n,e,t)=>(W(n,e,"default"),t&&W(t,e,"default")),d=(n,e,t)=>(t=n!=null?Se(Pe(n)):{},W(e||!n||!n.__esModule?R(t,"default",{value:n,enumerable:!0}):t,n)),Te=n=>W(R({},"__esModule",{value:!0}),n);var o=Ae((Oe,te)=>{te.exports=globalThis.luma});var F={};Ge(F,{WebGPUBuffer:()=>b,WebGPUDevice:()=>S,WebGPUSampler:()=>p,WebGPUShader:()=>P,WebGPUTexture:()=>C,webgpuAdapter:()=>be});O(F,d(o(),1));var f=d(o(),1);var X=d(o(),1);var M=d(o(),1);function _e(n){return n.byteLength||n.data?.byteLength||0}var b=class extends M.Buffer{device;handle;byteLength;constructor(e,t){super(e,t),this.device=e,this.byteLength=_e(t);let r=Boolean(t.data),i=Math.ceil(this.byteLength/4)*4;this.handle=this.props.handle||this.device.handle.createBuffer({size:i,usage:this.props.usage||GPUBufferUsage.VERTEX|GPUBufferUsage.COPY_DST,mappedAtCreation:this.props.mappedAtCreation||r,label:this.props.id}),t.data&&this._writeMapped(t.data),r&&!t.mappedAtCreation&&this.handle.unmap()}destroy(){this.handle?.destroy(),this.handle=null}write(e,t=0){this.device.handle.queue.writeBuffer(this.handle,t,e.buffer,e.byteOffset,e.byteLength)}async readAsync(e=0,t=this.byteLength){let r=new b(this.device,{usage:M.Buffer.MAP_READ|M.Buffer.COPY_DST,byteLength:t}),i=this.device.handle.createCommandEncoder();i.copyBufferToBuffer(this.handle,e,r.handle,0,t),this.device.handle.queue.submit([i.finish()]),await r.handle.mapAsync(GPUMapMode.READ,e,t);let s=r.handle.getMappedRange().slice(0);return r.handle.unmap(),r.destroy(),new Uint8Array(s)}_writeMapped(e){let t=this.handle.getMappedRange();new e.constructor(t).set(e)}mapAsync(e,t=0,r){return this.handle.mapAsync(e,t,r)}getMappedRange(e=0,t){return this.handle.getMappedRange(e,t)}unmap(){this.handle.unmap()}};var v=d(o(),1);function E(n){if(n.includes("webgl"))throw new Error("webgl-only format");return n}var re=d(o(),1),p=class extends re.Sampler{device;handle;constructor(e,t){super(e,t),this.device=e;let r={...this.props,mipmapFilter:void 0};t.type!=="comparison-sampler"&&delete r.compare,t.mipmapFilter&&t.mipmapFilter!=="none"&&(r.mipmapFilter=t.mipmapFilter),this.handle=this.handle||this.device.handle.createSampler(r),this.handle.label=this.props.id}destroy(){this.handle=null}};var ne=d(o(),1),_=class extends ne.TextureView{device;handle;texture;constructor(e,t){super(e,t),this.device=e,this.texture=t.texture,this.handle=this.handle||this.texture.handle.createView({format:t.format||this.texture.format,dimension:t.dimension||this.texture.dimension,aspect:t.aspect,baseMipLevel:t.baseMipLevel,mipLevelCount:t.mipLevelCount,baseArrayLayer:t.baseArrayLayer,arrayLayerCount:t.arrayLayerCount}),this.handle.label=this.props.id}destroy(){this.handle=null}};var De={"1d":"1d","2d":"2d","2d-array":"2d",cube:"2d","cube-array":"2d","3d":"3d"},C=class extends v.Texture{device;handle;sampler;view;constructor(e,t){super(e,t),this.device=e;let r={...this.props};t.data&&(r.data=t.data),this.initialize(r)}destroy(){this.handle?.destroy(),this.handle=null}createView(e){return new _(this.device,{...e,texture:this})}initialize(e){this.handle=this.props.handle||this.createHandle(),this.handle.label||=this.id,this.props.data&&(v.Texture.isExternalImage(this.props.data)?this.copyExternalImage({image:this.props.data}):this.setData({data:this.props.data})),this.width=this.handle.width,this.height=this.handle.height,this.sampler=e.sampler instanceof p?e.sampler:new p(this.device,e.sampler||{}),this.view=new _(this.device,{...this.props,texture:this})}createHandle(){let e=this.props.width||this.props.data?.width||1,t=this.props.height||this.props.data?.height||1;return this.device.handle.createTexture({label:this.id,size:{width:e,height:t,depthOrArrayLayers:this.depth},usage:this.props.usage||v.Texture.TEXTURE|v.Texture.COPY_DST,dimension:De[this.dimension],format:E(this.format),mipLevelCount:this.mipLevels,sampleCount:this.props.samples})}createGPUTextureView(){return this.handle.createView({label:this.id})}setSampler(e){return this.sampler=e instanceof p?e:new p(this.device,e),this}setTexture1DData(e){throw new Error("not implemented")}setTexture2DData(e,t,r){throw new Error("not implemented")}setTexture3DData(e,t,r){throw new Error("not implemented")}setTextureCubeData(e,t){throw new Error("not implemented")}setTextureArrayData(e){throw new Error("not implemented")}setTextureCubeArrayData(e){throw new Error("not implemented")}setData(e){if(ArrayBuffer.isView(e.data)){let t=new Uint8ClampedArray(e.data.buffer),r=new ImageData(t,this.width,this.height);return this.copyExternalImage({image:r})}throw new Error("Texture.setData: Use CommandEncoder to upload data to texture in WebGPU")}copyExternalImage(e){let t=v.Texture.getExternalImageSize(e.image),r={...v.Texture.defaultCopyExternalImageOptions,...t,...e},{image:i,sourceX:s,sourceY:a,width:l,height:c,depth:h,mipLevel:g,x:u,y:T,z:J,aspect:xe,colorSpace:we,premultipliedAlpha:ye,flipY:ve}=r;return this.device.handle.queue.copyExternalImageToTexture({source:i,origin:[s,a],flipY:ve},{texture:this.handle,origin:[u,T,J],mipLevel:g,aspect:xe,colorSpace:we,premultipliedAlpha:ye},[l,c,h]),{width:l,height:c}}};var ie=d(o(),1);var U=class extends ie.ExternalTexture{device;handle;sampler;constructor(e,t){super(e,t),this.device=e,this.handle=this.props.handle||this.device.handle.importExternalTexture({source:t.source,colorSpace:t.colorSpace}),this.sampler=null}destroy(){this.handle=null}setSampler(e){return this.sampler=e instanceof p?e:new p(this.device,e),this}};var D=d(o(),1),P=class extends D.Shader{device;handle;constructor(e,t){super(e,t),this.device=e;let r=t.source.includes("#version");if(this.props.language==="glsl"||r)throw new Error("GLSL shaders are not supported in WebGPU");this.device.handle.pushErrorScope("validation"),this.handle=this.props.handle||this.device.handle.createShaderModule({code:t.source}),this.device.handle.popErrorScope().then(i=>{i&&D.log.error(`${this} creation failed: | ||
"${i.message}"`,this,this.props.source)()}),this.handle.label=this.props.id,this._checkCompilationError()}get asyncCompilationStatus(){return this.getCompilationInfo().then(()=>this.compilationStatus)}async _checkCompilationError(){let e=await this.getCompilationInfo(),t=Boolean(e.find(r=>r.type==="error"));this.compilationStatus=t?"error":"success",this.debugShader(),this.compilationStatus==="error"&&D.log.error("Shader compilation error",e)()}destroy(){this.handle=null}async getCompilationInfo(){return(await this.handle.getCompilationInfo()).messages}};var w=d(o(),1);function m(n){return n.depthStencil=n.depthStencil||{format:"depth24plus",stencilFront:{},stencilBack:{},depthWriteEnabled:!1,depthCompare:"less-equal"},n.depthStencil}function k(n){return m(n).stencilFront}function V(n){return m(n).stencilBack}var Le={cullMode:(n,e,t)=>{t.primitive=t.primitive||{},t.primitive.cullMode=e},frontFace:(n,e,t)=>{t.primitive=t.primitive||{},t.primitive.frontFace=e},depthWriteEnabled:(n,e,t)=>{let r=m(t);r.depthWriteEnabled=e},depthCompare:(n,e,t)=>{let r=m(t);r.depthCompare=e},depthFormat:(n,e,t)=>{let r=m(t);r.format=e},depthBias:(n,e,t)=>{let r=m(t);r.depthBias=e},depthBiasSlopeScale:(n,e,t)=>{let r=m(t);r.depthBiasSlopeScale=e},depthBiasClamp:(n,e,t)=>{let r=m(t);r.depthBiasClamp=e},stencilReadMask:(n,e,t)=>{let r=m(t);r.stencilReadMask=e},stencilWriteMask:(n,e,t)=>{let r=m(t);r.stencilWriteMask=e},stencilCompare:(n,e,t)=>{let r=k(t),i=V(t);r.compare=e,i.compare=e},stencilPassOperation:(n,e,t)=>{let r=k(t),i=V(t);r.passOp=e,i.passOp=e},stencilFailOperation:(n,e,t)=>{let r=k(t),i=V(t);r.failOp=e,i.failOp=e},stencilDepthFailOperation:(n,e,t)=>{let r=k(t),i=V(t);r.depthFailOp=e,i.depthFailOp=e},sampleCount:(n,e,t)=>{t.multisample=t.multisample||{},t.multisample.count=e},sampleMask:(n,e,t)=>{t.multisample=t.multisample||{},t.multisample.mask=e},sampleAlphaToCoverageEnabled:(n,e,t)=>{t.multisample=t.multisample||{},t.multisample.alphaToCoverageEnabled=e},colorMask:(n,e,t)=>{let r=se(t);r[0].writeMask=e},blendColorOperation:(n,e,t)=>{se(t)}},Ie={primitive:{cullMode:"back",topology:"triangle-list"},vertex:{module:void 0,entryPoint:"main"},fragment:{module:void 0,entryPoint:"main",targets:[]},layout:"auto"};function ae(n,e={}){Object.assign(n,{...Ie,...n}),Fe(n,e)}function Fe(n,e){for(let[t,r]of Object.entries(e)){let i=Le[t];if(!i)throw new Error(`Illegal parameter ${t}`);i(t,r,n)}}function se(n){if(n.fragment.targets=n.fragment?.targets||[],!Array.isArray(n.fragment?.targets))throw new Error("colorstate");return n.fragment?.targets?.length===0&&n.fragment.targets?.push({}),n.fragment?.targets}var x=d(o(),1);function $(n,e,t,r){let i=We(r,t);return n.createBindGroup({layout:e,entries:i})}function oe(n,e,t){let r=n.bindings.find(i=>i.name===e||`${i.name}uniforms`===e.toLocaleLowerCase());return!r&&!t?.ignoreWarnings&&x.log.warn(`Binding ${e} not set: Not found in shader layout.`)(),r||null}function We(n,e){let t=[];for(let[r,i]of Object.entries(n)){let s=oe(e,r);s&&t.push(de(i,s.location)),s=oe(e,`${r}Sampler`,{ignoreWarnings:!0}),s&&t.push(de(i,s.location,{sampler:!0}))}return t}function de(n,e,t){if(n instanceof x.Buffer)return{binding:e,resource:{buffer:n.handle}};if(n instanceof x.Sampler)return{binding:e,resource:n.handle};if(n instanceof x.Texture)return t?.sampler?{binding:e,resource:n.sampler.handle}:{binding:e,resource:n.handle.createView({label:"bind-group-auto-created"})};throw new Error("invalid binding")}var B=d(o(),1);function Z(n){if(n.endsWith("-webgl"))throw new Error(`WebGPU does not support vertex format ${n}`);return n}function le(n,e){let t=[],r=new Set;for(let i of e){let s=[],a="vertex",l=0,c=i.format;if(i.attributes)for(let h of i.attributes){let g=h.attribute,u=he(n,g,r),T=u?.location;a=u?.stepMode||(u?.name.startsWith("instance")?"instance":"vertex"),s.push({format:Z(h.format||i.format),offset:h.byteOffset,shaderLocation:T}),l+=(0,B.decodeVertexFormat)(c).byteLength}else{let h=he(n,i.name,r);if(!h)continue;l=(0,B.decodeVertexFormat)(c).byteLength,a=h.stepMode||(h.name.startsWith("instance")?"instance":"vertex"),s.push({format:Z(c),offset:0,shaderLocation:h.location})}t.push({arrayStride:i.byteStride||l,stepMode:a,attributes:s})}for(let i of n.attributes)r.has(i.name)||t.push({arrayStride:(0,B.decodeVertexFormat)("float32x3").byteLength,stepMode:i.stepMode||(i.name.startsWith("instance")?"instance":"vertex"),attributes:[{format:Z("float32x3"),offset:0,shaderLocation:i.location}]});return t}function he(n,e,t){let r=n.attributes.find(i=>i.name===e);if(!r)return B.log.warn(`Supplied attribute not present in shader layout: ${e}`)(),null;if(t.has(e))throw new Error(`Found multiple entries for attribute: ${e}`);return t.add(e),r}var z=class extends w.RenderPipeline{device;handle;vs;fs=null;_bindings;_bindGroupLayout=null;_bindGroup=null;get[Symbol.toStringTag](){return"WebGPURenderPipeline"}constructor(e,t){if(super(e,t),this.device=e,this.handle=this.props.handle,!this.handle){let r=this._getRenderPipelineDescriptor();w.log.groupCollapsed(1,`new WebGPURenderPipeline(${this.id})`)(),w.log.probe(1,JSON.stringify(r,null,2))(),w.log.groupEnd(1)(),this.device.handle.pushErrorScope("validation"),this.handle=this.device.handle.createRenderPipeline(r),this.device.handle.popErrorScope().then(i=>{i&&w.log.error(`${this} creation failed: | ||
"${i.message}"`,this,this.props.vs?.source)()})}this.handle.label=this.props.id,this.vs=t.vs,this.fs=t.fs,this._bindings={...this.props.bindings}}destroy(){this.handle=null}setBindings(e){for(let[t,r]of Object.entries(e))this._bindings[t]!==r&&(this._bindGroup=null);Object.assign(this._bindings,e)}draw(e){let t=e.renderPass;this.device.handle.pushErrorScope("validation"),t.handle.setPipeline(this.handle),this.device.handle.popErrorScope().then(i=>{i&&w.log.error(`${this} setPipeline failed: | ||
"${i.message}"`,this)()});let r=this._getBindGroup();return r&&t.handle.setBindGroup(0,r),e.vertexArray.bindBeforeRender(e.renderPass),e.indexCount?t.handle.drawIndexed(e.indexCount,e.instanceCount,e.firstIndex,e.baseVertex,e.firstInstance):t.handle.draw(e.vertexCount||0,e.instanceCount||1,e.firstInstance),e.vertexArray.unbindAfterRender(e.renderPass),!0}_getBindGroup(){return this.shaderLayout.bindings.length===0?null:(this._bindGroupLayout=this._bindGroupLayout||this.handle.getBindGroupLayout(0),this._bindGroup=this._bindGroup||$(this.device.handle,this._bindGroupLayout,this.shaderLayout,this._bindings),this._bindGroup)}_getRenderPipelineDescriptor(){let e={module:this.props.vs.handle,entryPoint:this.props.vertexEntryPoint||"main",buffers:le(this.shaderLayout,this.props.bufferLayout)},t=[];if(this.props.colorAttachmentFormats)for(let a of this.props.colorAttachmentFormats)t.push(a?{format:E(a)}:null);else t.push({format:E(this.device.preferredColorFormat)});let r={module:this.props.fs.handle,entryPoint:this.props.fragmentEntryPoint||"main",targets:t},i={vertex:e,fragment:r,primitive:{topology:this.props.topology},layout:"auto"},s=this.props.depthStencilAttachmentFormat||this.device.preferredDepthFormat;return this.props.parameters.depthWriteEnabled&&(i.depthStencil={format:E(s)}),ae(i,this.props.parameters),i}};var pe=d(o(),1),A=class extends pe.Framebuffer{device;colorAttachments=[];depthStencilAttachment=null;constructor(e,t){super(e,t),this.device=e,this.autoCreateAttachmentTextures()}updateAttachments(){}};var ce=d(o(),1);var q=class extends ce.ComputePipeline{device;handle;_bindGroupLayout=null;_bindGroup=null;_bindings={};constructor(e,t){super(e,t),this.device=e;let r=this.props.shader;this.handle=this.props.handle||this.device.handle.createComputePipeline({label:this.props.id,compute:{module:r.handle,entryPoint:this.props.entryPoint,constants:this.props.constants},layout:"auto"})}setBindings(e){Object.assign(this._bindings,e)}_getBindGroup(){return this._bindGroupLayout=this._bindGroupLayout||this.handle.getBindGroupLayout(0),this._bindGroup=this._bindGroup||$(this.device.handle,this._bindGroupLayout,this.shaderLayout,this._bindings),this._bindGroup}};var y=d(o(),1),Q=class extends y.RenderPass{device;handle;pipeline=null;constructor(e,t={}){super(e,t),this.device=e;let r=t.framebuffer||e.getCanvasContext().getCurrentFramebuffer(),i=this.getRenderPassDescriptor(r),s=t.timestampQuerySet;if(s&&(i.occlusionQuerySet=s.handle),e.features.has("timestamp-query")){let a=t.timestampQuerySet;i.timestampWrites=a?{querySet:a.handle,beginningOfPassWriteIndex:t.beginTimestampIndex,endOfPassWriteIndex:t.endTimestampIndex}:void 0}if(!e.commandEncoder)throw new Error("commandEncoder not available");this.device.handle.pushErrorScope("validation"),this.handle=this.props.handle||e.commandEncoder.beginRenderPass(i),this.device.handle.popErrorScope().then(a=>{a&&y.log.error(`${this} creation failed: | ||
"${a.message}"`,this)()}),this.handle.label=this.props.id,y.log.groupCollapsed(3,`new WebGPURenderPass(${this.id})`)(),y.log.probe(3,JSON.stringify(i,null,2))(),y.log.groupEnd(3)()}destroy(){}end(){this.handle.end()}setPipeline(e){this.pipeline=e,this.handle.setPipeline(this.pipeline.handle)}setBindings(e){this.pipeline?.setBindings(e);let t=this.pipeline?._getBindGroup();t&&this.handle.setBindGroup(0,t)}setIndexBuffer(e,t,r=0,i){this.handle.setIndexBuffer(e.handle,t,r,i)}setVertexBuffer(e,t,r=0){this.handle.setVertexBuffer(e,t.handle,r)}draw(e){e.indexCount?this.handle.drawIndexed(e.indexCount,e.instanceCount,e.firstIndex,e.baseVertex,e.firstInstance):this.handle.draw(e.vertexCount||0,e.instanceCount||1,e.firstIndex,e.firstInstance)}drawIndirect(){}setParameters(e){let{blendConstant:t,stencilReference:r,scissorRect:i,viewport:s}=e;t&&this.handle.setBlendConstant(t),r&&this.handle.setStencilReference(r),i&&this.handle.setScissorRect(i[0],i[1],i[2],i[3]),s&&this.handle.setViewport(s[0],s[1],s[2],s[3],s[4],s[5])}pushDebugGroup(e){this.handle.pushDebugGroup(e)}popDebugGroup(){this.handle.popDebugGroup()}insertDebugMarker(e){this.handle.insertDebugMarker(e)}beginOcclusionQuery(e){this.handle.beginOcclusionQuery(e)}endOcclusionQuery(){this.handle.endOcclusionQuery()}getRenderPassDescriptor(e){let t={colorAttachments:[]};if(t.colorAttachments=e.colorAttachments.map((r,i)=>({loadOp:this.props.clearColor!==!1?"clear":"load",colorClearValue:this.props.clearColors?.[i]||this.props.clearColor||y.RenderPass.defaultClearColor,storeOp:this.props.discard?"discard":"store",view:r.handle})),e.depthStencilAttachment){t.depthStencilAttachment={view:e.depthStencilAttachment.handle};let{depthStencilAttachment:r}=t;this.props.depthReadOnly&&(r.depthReadOnly=!0),this.props.clearDepth!==!1&&(r.depthClearValue=this.props.clearDepth),!0&&(r.depthLoadOp=this.props.clearDepth!==!1?"clear":"load",r.depthStoreOp="store"),!1&&(r.stencilLoadOp=this.props.clearStencil!==!1?"clear":"load",r.stencilStoreOp="store")}return t}};var ue=d(o(),1),N=class extends ue.ComputePass{device;handle;_webgpuPipeline=null;constructor(e,t){super(e,t),this.device=e;let r;if(e.features.has("timestamp-query")){let i=t.timestampQuerySet;i&&(r={querySet:i.handle,beginningOfPassWriteIndex:t.beginTimestampIndex,endOfPassWriteIndex:t.endTimestampIndex})}this.handle=this.props.handle||e.commandEncoder?.beginComputePass({label:this.props.id,timestampWrites:r})}destroy(){}end(){this.handle.end()}setPipeline(e){let t=e;this.handle.setPipeline(t.handle),this._webgpuPipeline=t,this.setBindings([])}setBindings(e){let t=this._webgpuPipeline._getBindGroup();this.handle.setBindGroup(0,t)}dispatch(e,t,r){this.handle.dispatchWorkgroups(e,t,r)}dispatchIndirect(e,t=0){let r=e;this.handle.dispatchWorkgroupsIndirect(r.handle,t)}pushDebugGroup(e){this.handle.pushDebugGroup(e)}popDebugGroup(){this.handle.popDebugGroup()}insertDebugMarker(e){this.handle.insertDebugMarker(e)}};var L=d(o(),1);var dt=globalThis.document||{},ht=globalThis.process||{},lt=globalThis.console,me=globalThis.navigator||{};function j(n){if(typeof window<"u"&&window.process?.type==="renderer"||typeof process<"u"&&Boolean(process.versions?.electron))return!0;let e=typeof navigator<"u"&&navigator.userAgent,t=n||e;return Boolean(t&&t.indexOf("Electron")>=0)}function fe(){return!(typeof process=="object"&&String(process)==="[object process]"&&!process?.browser)||j()}function K(n){return!n&&!fe()?"Node":j(n)?"Electron":(n||me.userAgent||"").indexOf("Edge")>-1?"Edge":globalThis.chrome?"Chrome":globalThis.safari?"Safari":globalThis.mozInnerScreenX?"Firefox":"Unknown"}var H=class extends L.VertexArray{get[Symbol.toStringTag](){return"WebGPUVertexArray"}device;handle;constructor(e,t){super(e,t),this.device=e}destroy(){}setIndexBuffer(e){this.indexBuffer=e}setBuffer(e,t){this.attributes[e]=t}bindBeforeRender(e,t,r){let i=e,s=this.indexBuffer;s?.handle&&(L.log.info(3,"setting index buffer",s?.handle,s?.indexType)(),i.handle.setIndexBuffer(s?.handle,s?.indexType));for(let a=0;a<this.maxVertexAttributes;a++){let l=this.attributes[a];l?.handle&&(L.log.info(3,`setting vertex buffer ${a}`,l?.handle)(),i.handle.setVertexBuffer(a,l?.handle))}}unbindAfterRender(e){}static isConstantAttributeZeroSupported(e){return K()==="Chrome"}};var G=d(o(),1);var I=class extends G.CanvasContext{device;handle;depthStencilAttachment=null;get[Symbol.toStringTag](){return"WebGPUCanvasContext"}constructor(e,t,r){super(r),this.device=e,this._setAutoCreatedCanvasId(`${this.device.id}-canvas`),this.updateSize([this.drawingBufferWidth,this.drawingBufferHeight])}destroy(){this.handle.unconfigure()}getCurrentFramebuffer(e={depthStencilFormat:"depth24plus"}){let t=this.getCurrentTexture();if(t.width!==this.drawingBufferWidth||t.height!==this.drawingBufferHeight){let[r,i]=this.getDrawingBufferSize();this.drawingBufferWidth=t.width,this.drawingBufferHeight=t.height,G.log.log(1,`${this}: Resized to compensate for initial canvas size mismatch ${r}x${i} => ${this.drawingBufferWidth}x${this.drawingBufferHeight}px`)()}return e?.depthStencilFormat&&this._createDepthStencilAttachment(e?.depthStencilFormat),new A(this.device,{colorAttachments:[t],depthStencilAttachment:this.depthStencilAttachment})}updateSize(e){this.depthStencilAttachment&&(this.depthStencilAttachment.destroy(),this.depthStencilAttachment=null),this.handle.configure({device:this.device.handle,format:this.device.preferredColorFormat,colorSpace:this.props.colorSpace,alphaMode:this.props.alphaMode})}resize(e){if(this.device.handle&&!this.props.autoResize&&this.canvas){let t=this.getDevicePixelRatio(e?.useDevicePixels);this._setDevicePixelRatio(t,e);return}}getCurrentTexture(){return this.device.createTexture({id:`${this.id}#color-texture`,handle:this.handle.getCurrentTexture(),format:this.device.preferredColorFormat})}_createDepthStencilAttachment(e){return this.depthStencilAttachment||(this.depthStencilAttachment=this.device.createTexture({id:`${this.id}#depth-stencil-texture`,usage:G.Texture.RENDER_ATTACHMENT,format:e,width:this.drawingBufferWidth,height:this.drawingBufferHeight})),this.depthStencilAttachment}};var ge=d(o(),1),Y=class extends ge.QuerySet{device;handle;constructor(e,t){super(e,t),this.device=e,this.handle=this.props.handle||this.device.handle.createQuerySet({type:this.props.type,count:this.props.count}),this.handle.label=this.props.id}destroy(){this.handle?.destroy(),this.handle=null}};var S=class extends X.Device{handle;type="webgpu";preferredColorFormat=navigator.gpu.getPreferredCanvasFormat();preferredDepthFormat="depth24plus";features;info;limits;lost;renderPass=null;canvasContext;_isLost=!1;commandEncoder=null;adapter;adapterInfo;constructor(e,t,r,i){if(super({...e,id:e.id||"webgpu-device"}),this.handle=t,this.adapter=r,this.adapterInfo=i,this.info=this._getInfo(),this.features=this._getFeatures(),this.limits=this.handle.limits,t.addEventListener("uncapturederror",s=>{let a=s instanceof GPUUncapturedErrorEvent?s.error.message:"Unknown WebGPU error";if(this.reportError(new Error(a)),this.props.debug)debugger;s.preventDefault()}),this.lost=new Promise(async s=>{let a=await this.handle.lost;this._isLost=!0,s({reason:"destroyed",message:a.message})}),e.createCanvasContext){let s=e.createCanvasContext===!0?{}:e.createCanvasContext;this.canvasContext=new I(this,this.adapter,s)}}destroy(){this.handle.destroy()}get isLost(){return this._isLost}createBuffer(e){let t=this._normalizeBufferProps(e);return new b(this,t)}createTexture(e){return new C(this,e)}createExternalTexture(e){return new U(this,e)}createShader(e){return new P(this,e)}createSampler(e){return new p(this,e)}createRenderPipeline(e){return new z(this,e)}createFramebuffer(e){return new A(this,e)}createComputePipeline(e){return new q(this,e)}createVertexArray(e){return new H(this,e)}beginRenderPass(e){return this.commandEncoder=this.commandEncoder||this.handle.createCommandEncoder(),new Q(this,e)}beginComputePass(e){return this.commandEncoder=this.commandEncoder||this.handle.createCommandEncoder(),new N(this,e)}createTransformFeedback(e){throw new Error("Transform feedback not supported in WebGPU")}createQuerySet(e){return new Y(this,e)}createCanvasContext(e){return new I(this,this.adapter,e)}submit(){let e=this.commandEncoder?.finish();e&&(this.handle.pushErrorScope("validation"),this.handle.queue.submit([e]),this.handle.popErrorScope().then(t=>{t&&this.reportError(new Error(`WebGPU command submission failed: ${t.message}`))})),this.commandEncoder=null}_getInfo(){let[e,t]=(this.adapterInfo.driver||"").split(" Version "),r=this.adapterInfo.vendor||this.adapter.__brand||"unknown",i=e||"",s=t||"",a=r==="apple"?"apple":"unknown",l=this.adapterInfo.architecture||"unknown",c=this.adapterInfo.backend||"unknown",h=(this.adapterInfo.type||"").split(" ")[0].toLowerCase()||"unknown";return{type:"webgpu",vendor:r,renderer:i,version:s,gpu:a,gpuType:h,gpuBackend:c,gpuArchitecture:l,shadingLanguage:"wgsl",shadingLanguageVersion:100}}_getFeatures(){let e=new Set(this.handle.features);e.has("depth-clamping")&&(e.delete("depth-clamping"),e.add("depth-clip-control")),e.has("texture-compression-bc")&&e.add("texture-compression-bc5-webgl");let t=["timer-query-webgl","compilation-status-async-webgl","float32-renderable-webgl","float16-renderable-webgl","norm16-renderable-webgl","texture-filterable-anisotropic-webgl","shader-noperspective-interpolation-webgl"];for(let r of t)e.add(r);return new X.DeviceFeatures(Array.from(e),this.props._disabledFeatures)}_getDeviceSpecificTextureFormatCapabilities(e){let{format:t}=e;return t.includes("webgl")?{format:t,create:!1,render:!1,filter:!1,blend:!1,store:!1}:e}copyExternalImageToTexture(e){let{source:t,sourceX:r=0,sourceY:i=0,texture:s,mipLevel:a=0,aspect:l="all",colorSpace:c="display-p3",premultipliedAlpha:h=!1,width:g=s.width,height:u=s.height,depth:T=1}=e,J=s;this.handle?.queue.copyExternalImageToTexture({source:t,origin:[r,i]},{texture:J.handle,origin:[0,0,0],mipLevel:a,aspect:l,colorSpace:c,premultipliedAlpha:h},[g,u,T])}};var ee=class extends f.Adapter{type="webgpu";constructor(){super(),S.adapter=this}isSupported(){return Boolean(typeof navigator<"u"&&navigator.gpu)}async create(e){if(!navigator.gpu)throw new Error("WebGPU not available. Open in Chrome Canary and turn on chrome://flags/#enable-unsafe-webgpu");f.log.groupCollapsed(1,"WebGPUDevice created")();let t=await navigator.gpu.requestAdapter({powerPreference:"high-performance"});if(!t)throw new Error("Failed to request WebGPU adapter");let r=await t.requestAdapterInfo();f.log.probe(2,"Adapter available",r)();let i=[],s={};if(e._requestMaxLimits){i.push(...Array.from(t.features));let c=Object.keys(t.limits).filter(h=>!["minSubgroupSize","maxSubgroupSize"].includes(h));for(let h of c){let g=h,u=t.limits[g];typeof u=="number"&&(s[g]=u)}}let a=await t.requestDevice({requiredFeatures:i,requiredLimits:s});f.log.probe(1,"GPUDevice available")();let l=new S(e,a,t,r);return f.log.probe(1,"Device created. For more info, set chrome://flags/#enable-webgpu-developer-features")(),f.log.table(1,l.info)(),f.log.groupEnd(1)(),l}async attach(e){throw new Error("WebGPUAdapter.attach() not implemented")}},be=new ee;return Te(F);})(); | ||
return __exports__; | ||
}); |
@@ -11,1 +11,2 @@ // luma.gl | ||
export { WebGPUShader } from "./adapter/resources/webgpu-shader.js"; | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "@luma.gl/webgpu", | ||
"version": "9.1.0-alpha.19", | ||
"version": "9.1.0-beta.1", | ||
"description": "WebGPU adapter for the luma.gl core API", | ||
@@ -40,3 +40,3 @@ "type": "module", | ||
"peerDependencies": { | ||
"@luma.gl/core": "9.1.0-alpha.17" | ||
"@luma.gl/core": "9.1.0-alpha.19" | ||
}, | ||
@@ -47,3 +47,3 @@ "dependencies": { | ||
}, | ||
"gitHead": "c836c09a5105e2515a036abff4a1b926d245a152" | ||
"gitHead": "e04bfb138f96d690992f0e0a67b358cbe23bd59f" | ||
} |
@@ -45,3 +45,4 @@ // luma.gl | ||
shaderLayout: ComputeShaderLayout, | ||
bindingName: string | ||
bindingName: string, | ||
options?: {ignoreWarnings?: boolean} | ||
): BindingDeclaration | null { | ||
@@ -52,3 +53,3 @@ const bindingLayout = shaderLayout.bindings.find( | ||
); | ||
if (!bindingLayout) { | ||
if (!bindingLayout && !options?.ignoreWarnings) { | ||
log.warn(`Binding ${bindingName} not set: Not found in shader layout.`)(); | ||
@@ -70,6 +71,14 @@ } | ||
for (const [bindingName, value] of Object.entries(bindings)) { | ||
const bindingLayout = getShaderLayoutBinding(shaderLayout, bindingName); | ||
let bindingLayout = getShaderLayoutBinding(shaderLayout, bindingName); | ||
if (bindingLayout) { | ||
entries.push(getBindGroupEntry(value, bindingLayout.location)); | ||
} | ||
// TODO - hack to automatically bind samplers to supplied texture default samplers | ||
bindingLayout = getShaderLayoutBinding(shaderLayout, `${bindingName}Sampler`, { | ||
ignoreWarnings: true | ||
}); | ||
if (bindingLayout) { | ||
entries.push(getBindGroupEntry(value, bindingLayout.location, {sampler: true})); | ||
} | ||
} | ||
@@ -80,3 +89,7 @@ | ||
function getBindGroupEntry(binding: Binding, index: number): GPUBindGroupEntry { | ||
function getBindGroupEntry( | ||
binding: Binding, | ||
index: number, | ||
options?: {sampler?: boolean} | ||
): GPUBindGroupEntry { | ||
if (binding instanceof Buffer) { | ||
@@ -96,2 +109,8 @@ return { | ||
} else if (binding instanceof Texture) { | ||
if (options?.sampler) { | ||
return { | ||
binding: index, | ||
resource: (binding as WebGPUTexture).sampler.handle | ||
}; | ||
} | ||
return { | ||
@@ -98,0 +117,0 @@ binding: index, |
@@ -155,7 +155,7 @@ // luma.gl | ||
if (!attribute) { | ||
log.warn(`Unknown attribute ${name}`)(); | ||
log.warn(`Supplied attribute not present in shader layout: ${name}`)(); | ||
return null; | ||
} | ||
if (attributeNames.has(name)) { | ||
throw new Error(`Duplicate attribute ${name}`); | ||
throw new Error(`Found multiple entries for attribute: ${name}`); | ||
} | ||
@@ -162,0 +162,0 @@ attributeNames.add(name); |
@@ -49,3 +49,9 @@ // luma.gl | ||
this.device.handle.pushErrorScope('validation'); | ||
this.handle = this.props.handle || device.commandEncoder.beginRenderPass(renderPassDescriptor); | ||
this.device.handle.popErrorScope().then((error: GPUError | null) => { | ||
if (error) { | ||
log.error(`${this} creation failed:\n"${error.message}"`, this)(); | ||
} | ||
}); | ||
this.handle.label = this.props.id; | ||
@@ -52,0 +58,0 @@ log.groupCollapsed(3, `new WebGPURenderPass(${this.id})`)(); |
@@ -33,2 +33,6 @@ // luma.gl MIT license | ||
override get [Symbol.toStringTag]() { | ||
return 'WebGPURenderPipeline'; | ||
} | ||
constructor(device: WebGPUDevice, props: RenderPipelineProps) { | ||
@@ -43,6 +47,14 @@ super(device, props); | ||
log.groupEnd(1)(); | ||
this.device.handle.pushErrorScope('validation'); | ||
this.handle = this.device.handle.createRenderPipeline(descriptor); | ||
this.device.handle.popErrorScope().then((error: GPUError | null) => { | ||
if (error) { | ||
log.error(`${this} creation failed:\n"${error.message}"`, this, this.props.vs?.source)(); | ||
} | ||
}); | ||
} | ||
this.handle.label = this.props.id; | ||
// Note: Often the same shader in WebGPU | ||
this.vs = props.vs as WebGPUShader; | ||
@@ -65,2 +77,8 @@ this.fs = props.fs as WebGPUShader; | ||
setBindings(bindings: Record<string, Binding>): void { | ||
// Invalidate the cached bind group if any value has changed | ||
for (const [name, binding] of Object.entries(bindings)) { | ||
if (this._bindings[name] !== binding) { | ||
this._bindGroup = null; | ||
} | ||
} | ||
Object.assign(this._bindings, bindings); | ||
@@ -84,3 +102,9 @@ } | ||
// Set pipeline | ||
this.device.handle.pushErrorScope('validation'); | ||
webgpuRenderPass.handle.setPipeline(this.handle); | ||
this.device.handle.popErrorScope().then((error: GPUError | null) => { | ||
if (error) { | ||
log.error(`${this} setPipeline failed:\n"${error.message}"`, this)(); | ||
} | ||
}); | ||
@@ -149,2 +173,13 @@ // Set bindings (uniform buffers, textures etc) | ||
// Populate color targets | ||
// TODO - at the moment blend and write mask are only set on the first target | ||
const targets: (GPUColorTargetState | null)[] = []; | ||
if (this.props.colorAttachmentFormats) { | ||
for (const format of this.props.colorAttachmentFormats) { | ||
targets.push(format ? {format: getWebGPUTextureFormat(format)} : null); | ||
} | ||
} else { | ||
targets.push({format: getWebGPUTextureFormat(this.device.preferredColorFormat)}); | ||
} | ||
// Set up the fragment stage | ||
@@ -154,8 +189,3 @@ const fragment: GPUFragmentState = { | ||
entryPoint: this.props.fragmentEntryPoint || 'main', | ||
targets: [ | ||
{ | ||
// TODO exclamation mark hack! | ||
format: getWebGPUTextureFormat(this.device.getCanvasContext().format) | ||
} | ||
] | ||
targets | ||
}; | ||
@@ -173,2 +203,11 @@ | ||
// Set depth format if required, defaulting to the preferred depth format | ||
const depthFormat = this.props.depthStencilAttachmentFormat || this.device.preferredDepthFormat; | ||
if (this.props.parameters.depthWriteEnabled) { | ||
descriptor.depthStencil = { | ||
format: getWebGPUTextureFormat(depthFormat) | ||
}; | ||
} | ||
// Set parameters on the descriptor | ||
@@ -175,0 +214,0 @@ applyParametersToRenderPipelineDescriptor(descriptor, this.props.parameters); |
@@ -20,8 +20,17 @@ // luma.gl | ||
const isGLSL = props.source.includes('#version'); | ||
if (this.props.language === 'glsl' || isGLSL) { | ||
throw new Error('GLSL shaders are not supported in WebGPU'); | ||
} | ||
this.device.handle.pushErrorScope('validation'); | ||
this.handle = this.props.handle || this.device.handle.createShaderModule({code: props.source}); | ||
this.device.handle.popErrorScope().then((error: GPUError | null) => { | ||
if (error) { | ||
log.error(`${this} creation failed:\n"${error.message}"`, this, this.props.source)(); | ||
} | ||
}); | ||
this.handle = this.props.handle || this.createHandle(); | ||
this.handle.label = this.props.id; | ||
this._checkCompilationError(this.device.handle.popErrorScope()); | ||
this._checkCompilationError(); | ||
} | ||
@@ -33,13 +42,13 @@ | ||
async _checkCompilationError(errorScope: Promise<GPUError | null>): Promise<void> { | ||
const error = (await errorScope) as GPUValidationError; | ||
if (error) { | ||
// The `Shader` base class will determine if debug window should be opened based on props | ||
this.debugShader(); | ||
async _checkCompilationError(): Promise<void> { | ||
const shaderLog = await this.getCompilationInfo(); | ||
const hasErrors = Boolean(shaderLog.find(msg => msg.type === 'error')); | ||
this.compilationStatus = hasErrors ? 'error' : 'success'; | ||
this.debugShader(); | ||
const shaderLog = await this.getCompilationInfo(); | ||
log.error(`Shader compilation error: ${error.message}`, shaderLog)(); | ||
if (this.compilationStatus === 'error') { | ||
log.error(`Shader compilation error`, shaderLog)(); | ||
// Note: Even though this error is asynchronous and thrown after the constructor completes, | ||
// it will result in a useful stack trace leading back to the constructor | ||
throw new Error(`Shader compilation error: ${error.message}`); | ||
// throw new Error(`Shader compilation error`); | ||
} | ||
@@ -60,15 +69,2 @@ } | ||
} | ||
// PRIVATE METHODS | ||
protected createHandle(): GPUShaderModule { | ||
const {source} = this.props; | ||
const isGLSL = source.includes('#version'); | ||
if (this.props.language === 'glsl' || isGLSL) { | ||
throw new Error('GLSL shaders are not supported in WebGPU'); | ||
} | ||
return this.device.handle.createShaderModule({code: source}); | ||
} | ||
} |
@@ -60,3 +60,8 @@ // luma.gl | ||
// Note we can't unset an index buffer | ||
log.warn('setting index buffer', webgpuIndexBuffer?.handle, webgpuIndexBuffer?.indexType)(); | ||
log.info( | ||
3, | ||
'setting index buffer', | ||
webgpuIndexBuffer?.handle, | ||
webgpuIndexBuffer?.indexType | ||
)(); | ||
webgpuRenderPass.handle.setIndexBuffer( | ||
@@ -71,3 +76,3 @@ webgpuIndexBuffer?.handle, | ||
if (webgpuBuffer?.handle) { | ||
log.warn(`setting vertex buffer ${location}`, webgpuBuffer?.handle)(); | ||
log.info(3, `setting vertex buffer ${location}`, webgpuBuffer?.handle)(); | ||
webgpuRenderPass.handle.setVertexBuffer(location, webgpuBuffer?.handle); | ||
@@ -74,0 +79,0 @@ } |
@@ -7,5 +7,4 @@ // luma.gl | ||
import type {Texture, TextureFormat, CanvasContextProps} from '@luma.gl/core'; | ||
import {CanvasContext, log} from '@luma.gl/core'; | ||
import {getWebGPUTextureFormat} from './helpers/convert-texture-format'; | ||
import type {DepthStencilTextureFormat, CanvasContextProps} from '@luma.gl/core'; | ||
import {CanvasContext, Texture, log} from '@luma.gl/core'; | ||
import {WebGPUDevice} from './webgpu-device'; | ||
@@ -22,23 +21,17 @@ import {WebGPUFramebuffer} from './resources/webgpu-framebuffer'; | ||
readonly device: WebGPUDevice; | ||
readonly gpuCanvasContext: GPUCanvasContext; | ||
/** Format of returned textures: "bgra8unorm", "rgba8unorm", "rgba16float". */ | ||
readonly format: TextureFormat = navigator.gpu.getPreferredCanvasFormat() as TextureFormat; | ||
/** Default stencil format for depth textures */ | ||
readonly depthStencilFormat: TextureFormat = 'depth24plus'; | ||
readonly handle: GPUCanvasContext; | ||
private depthStencilAttachment: Texture | null = null; | ||
private depthStencilAttachment: WebGPUTexture | null = null; | ||
get [Symbol.toStringTag](): string { | ||
return 'WebGPUCanvasContext'; | ||
} | ||
constructor(device: WebGPUDevice, adapter: GPUAdapter, props: CanvasContextProps) { | ||
super(props); | ||
this.device = device; | ||
// TODO - hack to trigger resize? | ||
this.width = -1; | ||
this.height = -1; | ||
// Base class constructor cannot access derived methods/fields, so we need to call these functions in the subclass constructor | ||
this._setAutoCreatedCanvasId(`${this.device.id}-canvas`); | ||
// @ts-ignore TODO - we don't handle OffscreenRenderingContext. | ||
this.gpuCanvasContext = this.canvas.getContext('webgpu'); | ||
// TODO this has been replaced | ||
// this.format = this.gpuCanvasContext.getPreferredFormat(adapter); | ||
this.format = 'bgra8unorm'; | ||
this.updateSize([this.drawingBufferWidth, this.drawingBufferHeight]); | ||
} | ||
@@ -48,26 +41,31 @@ | ||
destroy(): void { | ||
this.gpuCanvasContext.unconfigure(); | ||
this.handle.unconfigure(); | ||
} | ||
/** Update framebuffer with properly resized "swap chain" texture views */ | ||
getCurrentFramebuffer(): WebGPUFramebuffer { | ||
// Ensure the canvas context size is updated | ||
this.update(); | ||
getCurrentFramebuffer( | ||
options: {depthStencilFormat?: DepthStencilTextureFormat | false} = { | ||
depthStencilFormat: 'depth24plus' | ||
} | ||
): WebGPUFramebuffer { | ||
// Wrap the current canvas context texture in a luma.gl texture | ||
// const currentColorAttachment = this.device.createTexture({ | ||
// id: 'default-render-target', | ||
// handle: this.gpuCanvasContext.getCurrentTexture(), | ||
// format: this.format, | ||
// width: this.width, | ||
// height: this.height | ||
// }); | ||
// Wrap the current canvas context texture in a luma.gl texture | ||
const currentColorAttachment = this.getCurrentTexture(); | ||
this.width = currentColorAttachment.width; | ||
this.height = currentColorAttachment.height; | ||
// TODO - temporary debug code | ||
if ( | ||
currentColorAttachment.width !== this.drawingBufferWidth || | ||
currentColorAttachment.height !== this.drawingBufferHeight | ||
) { | ||
const [oldWidth, oldHeight] = this.getDrawingBufferSize(); | ||
this.drawingBufferWidth = currentColorAttachment.width; | ||
this.drawingBufferHeight = currentColorAttachment.height; | ||
log.log( | ||
1, | ||
`${this}: Resized to compensate for initial canvas size mismatch ${oldWidth}x${oldHeight} => ${this.drawingBufferWidth}x${this.drawingBufferHeight}px` | ||
)(); | ||
} | ||
// Resize the depth stencil attachment | ||
this._createDepthStencilAttachment(); | ||
if (options?.depthStencilFormat) { | ||
this._createDepthStencilAttachment(options?.depthStencilFormat); | ||
} | ||
@@ -81,40 +79,31 @@ return new WebGPUFramebuffer(this.device, { | ||
/** Resizes and updates render targets if necessary */ | ||
update() { | ||
const [width, height] = this.getPixelSize(); | ||
updateSize(size: [newWidth: number, newHeight: number]): void { | ||
if (this.depthStencilAttachment) { | ||
this.depthStencilAttachment.destroy(); | ||
this.depthStencilAttachment = null; | ||
} | ||
const sizeChanged = width !== this.width || height !== this.height; | ||
if (sizeChanged) { | ||
this.width = width; | ||
this.height = height; | ||
if (this.depthStencilAttachment) { | ||
this.depthStencilAttachment.destroy(); | ||
this.depthStencilAttachment = null; | ||
} | ||
// Reconfigure the canvas size. | ||
// https://www.w3.org/TR/webgpu/#canvas-configuration | ||
this.gpuCanvasContext.configure({ | ||
device: this.device.handle, | ||
format: getWebGPUTextureFormat(this.format), | ||
// Can be used to define e.g. -srgb views | ||
// viewFormats: [...] | ||
colorSpace: this.props.colorSpace, | ||
alphaMode: this.props.alphaMode | ||
}); | ||
log.log(1, `Resized to ${this.width}x${this.height}px`)(); | ||
} | ||
// Reconfigure the canvas size. | ||
// https://www.w3.org/TR/webgpu/#canvas-configuration | ||
this.handle.configure({ | ||
device: this.device.handle, | ||
format: this.device.preferredColorFormat, | ||
// Can be used to define e.g. -srgb views | ||
// viewFormats: [...] | ||
colorSpace: this.props.colorSpace, | ||
alphaMode: this.props.alphaMode | ||
}); | ||
} | ||
resize(options?: {width?: number; height?: number; useDevicePixels?: boolean | number}): void { | ||
this.update(); | ||
if (!this.device.handle) return; | ||
if (this.props.autoResize) { | ||
return; | ||
} | ||
// Resize browser context . | ||
if (this.canvas) { | ||
const devicePixelRatio = this.getDevicePixelRatio(options?.useDevicePixels); | ||
this.setDevicePixelRatio(devicePixelRatio, options); | ||
this._setDevicePixelRatio(devicePixelRatio, options); | ||
return; | ||
@@ -128,4 +117,4 @@ } | ||
id: `${this.id}#color-texture`, | ||
handle: this.gpuCanvasContext.getCurrentTexture(), | ||
format: this.format | ||
handle: this.handle.getCurrentTexture(), | ||
format: this.device.preferredColorFormat | ||
}); | ||
@@ -135,10 +124,10 @@ } | ||
/** We build render targets on demand (i.e. not when size changes but when about to render) */ | ||
_createDepthStencilAttachment() { | ||
_createDepthStencilAttachment(depthStencilFormat: DepthStencilTextureFormat): WebGPUTexture { | ||
if (!this.depthStencilAttachment) { | ||
this.depthStencilAttachment = this.device.createTexture({ | ||
id: `${this.id}#depth-stencil-texture`, | ||
format: this.depthStencilFormat, | ||
width: this.width, | ||
height: this.height, | ||
usage: GPUTextureUsage.RENDER_ATTACHMENT | ||
usage: Texture.RENDER_ATTACHMENT, | ||
format: depthStencilFormat, | ||
width: this.drawingBufferWidth, | ||
height: this.drawingBufferHeight | ||
}); | ||
@@ -145,0 +134,0 @@ } |
@@ -12,2 +12,3 @@ // luma.gl | ||
DeviceFeature, | ||
DeviceTextureFormatCapabilities, | ||
CanvasContextProps, | ||
@@ -19,3 +20,2 @@ BufferProps, | ||
TextureProps, | ||
TextureFormat, | ||
ExternalTextureProps, | ||
@@ -33,3 +33,3 @@ FramebufferProps, | ||
QuerySetProps, | ||
DeviceProps | ||
DeviceProps, | ||
} from '@luma.gl/core'; | ||
@@ -55,11 +55,11 @@ import {Device, DeviceFeatures} from '@luma.gl/core'; | ||
export class WebGPUDevice extends Device { | ||
/** The underlying WebGPU device */ | ||
readonly handle: GPUDevice; | ||
/** type of this device */ | ||
readonly type = 'webgpu'; | ||
/** The underlying WebGPU device */ | ||
readonly handle: GPUDevice; | ||
/* The underlying WebGPU adapter */ | ||
readonly adapter: GPUAdapter; | ||
/* The underlying WebGPU adapter's info */ | ||
readonly adapterInfo: GPUAdapterInfo; | ||
readonly preferredColorFormat = navigator.gpu.getPreferredCanvasFormat() as | ||
| 'rgba8unorm' | ||
| 'bgra8unorm'; | ||
readonly preferredDepthFormat = 'depth24plus'; | ||
@@ -71,7 +71,13 @@ readonly features: DeviceFeatures; | ||
readonly lost: Promise<{reason: 'destroyed'; message: string}>; | ||
canvasContext: WebGPUCanvasContext | null = null; | ||
renderPass: WebGPURenderPass | null = null; | ||
override canvasContext: WebGPUCanvasContext | null; | ||
private _isLost: boolean = false; | ||
// canvasContext: WebGPUCanvasContext | null = null; | ||
commandEncoder: GPUCommandEncoder | null = null; | ||
renderPass: WebGPURenderPass | null = null; | ||
/* The underlying WebGPU adapter */ | ||
readonly adapter: GPUAdapter; | ||
/* The underlying WebGPU adapter's info */ | ||
readonly adapterInfo: GPUAdapterInfo; | ||
@@ -97,4 +103,9 @@ constructor( | ||
const errorMessage = | ||
event instanceof GPUUncapturedErrorEvent ? event.error.message : 'Unknown error'; | ||
this.error(new Error(errorMessage)); | ||
event instanceof GPUUncapturedErrorEvent ? event.error.message : 'Unknown WebGPU error'; | ||
this.reportError(new Error(errorMessage)); | ||
if (this.props.debug) { | ||
// eslint-disable-next-line no-debugger | ||
debugger; | ||
} | ||
event.preventDefault(); | ||
}); | ||
@@ -126,20 +137,2 @@ | ||
isTextureFormatSupported(format: TextureFormat): boolean { | ||
return !format.includes('webgl'); | ||
} | ||
/** @todo implement proper check? */ | ||
isTextureFormatFilterable(format: TextureFormat): boolean { | ||
return ( | ||
this.isTextureFormatSupported(format) && | ||
!format.startsWith('depth') && | ||
!format.startsWith('stencil') | ||
); | ||
} | ||
/** @todo implement proper check? */ | ||
isTextureFormatRenderable(format: TextureFormat): boolean { | ||
return this.isTextureFormatSupported(format); | ||
} | ||
get isLost(): boolean { | ||
@@ -219,9 +212,13 @@ return this._isLost; | ||
submit(): void { | ||
// this.renderPass?.end(); | ||
const commandBuffer = this.commandEncoder?.finish(); | ||
if (commandBuffer) { | ||
this.handle.pushErrorScope('validation'); | ||
this.handle.queue.submit([commandBuffer]); | ||
this.handle.popErrorScope().then((error: GPUError | null) => { | ||
if (error) { | ||
this.reportError(new Error(`WebGPU command submission failed: ${error.message}`)); | ||
} | ||
}); | ||
} | ||
this.commandEncoder = null; | ||
// this.renderPass = null; | ||
} | ||
@@ -291,2 +288,15 @@ | ||
override _getDeviceSpecificTextureFormatCapabilities( | ||
capabilities: DeviceTextureFormatCapabilities | ||
): DeviceTextureFormatCapabilities { | ||
const {format} = capabilities; | ||
if (format.includes('webgl')) { | ||
return {format, create: false, render: false, filter: false, blend: false, store: false}; | ||
} | ||
return capabilities; | ||
} | ||
// DEPRECATED METHODS | ||
// @deprecated | ||
copyExternalImageToTexture(options: { | ||
@@ -293,0 +303,0 @@ texture: Texture; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
534141
122
8592