refactor
This commit is contained in:
parent
c039544ec6
commit
722a484bb5
10 changed files with 430 additions and 544 deletions
2
package-lock.json
generated
2
package-lock.json
generated
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"name": "canvas",
|
"name": "untitled-game-engine",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
|
|
427
src/plugins/renderer/engine/webgpu/context.js
Normal file
427
src/plugins/renderer/engine/webgpu/context.js
Normal file
|
@ -0,0 +1,427 @@
|
||||||
|
export class WebGPUError extends Error {
|
||||||
|
/**
|
||||||
|
* @param {string} message
|
||||||
|
*/
|
||||||
|
constructor(message) {
|
||||||
|
super(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsupported() {
|
||||||
|
return new WebGPUError("WebGPU is unsupported in this browser")
|
||||||
|
}
|
||||||
|
|
||||||
|
static adapterUnavailable() {
|
||||||
|
return new WebGPUError("Could not request a WebGPU adapter.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class WebGPUBuilderError extends Error {
|
||||||
|
/**
|
||||||
|
* @param {string} message
|
||||||
|
*/
|
||||||
|
constructor(message) {
|
||||||
|
super(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} property
|
||||||
|
*/
|
||||||
|
static missing(property) {
|
||||||
|
return new WebGPUBuilderError(`Missing required property: ${property}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GPUCanvasConfigurationBuilder {
|
||||||
|
/** @type {WebGPUContextBuilder} */
|
||||||
|
#contextBuilder
|
||||||
|
/** @type {GPUTextureUsageFlags} */
|
||||||
|
#usage = 0x10
|
||||||
|
/** @type {GPUTextureFormat[]} */
|
||||||
|
#viewFormats = []
|
||||||
|
/** @type {PredefinedColorSpace} */
|
||||||
|
#colorSpace = 'srgb'
|
||||||
|
/** @type {GPUCanvasToneMapping} */
|
||||||
|
#toneMapping = {}
|
||||||
|
/** @type {GPUCanvasAlphaMode} */
|
||||||
|
#alphaMode = 'opaque'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {WebGPUContextBuilder} builder
|
||||||
|
*/
|
||||||
|
constructor(builder) {
|
||||||
|
this.#contextBuilder = builder
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {GPUTextureUsageFlags} usage
|
||||||
|
*/
|
||||||
|
usage(usage) {
|
||||||
|
this.#usage = usage
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {GPUTextureUsageFlags} usage
|
||||||
|
*/
|
||||||
|
addUsage(usage) {
|
||||||
|
this.#usage = this.#usage | usage
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {GPUTextureFormat[]} formats
|
||||||
|
*/
|
||||||
|
viewFormats(formats) {
|
||||||
|
this.#viewFormats = formats
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {GPUTextureFormat} format
|
||||||
|
*/
|
||||||
|
addViewFormat(format) {
|
||||||
|
this.#viewFormats.push(format)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {PredefinedColorSpace} space
|
||||||
|
*/
|
||||||
|
colorSpace(space) {
|
||||||
|
this.#colorSpace = space
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {GPUCanvasToneMappingMode} mode
|
||||||
|
*/
|
||||||
|
toneMappingMode(mode) {
|
||||||
|
this.#toneMapping = { mode }
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {GPUCanvasAlphaMode} mode
|
||||||
|
*/
|
||||||
|
alphaMode(mode) {
|
||||||
|
this.#alphaMode = mode
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {GPUCanvasBuilderConfiguration}
|
||||||
|
*/
|
||||||
|
build() {
|
||||||
|
return {
|
||||||
|
usage: this.#usage,
|
||||||
|
viewFormats: this.#viewFormats,
|
||||||
|
colorSpace: this.#colorSpace,
|
||||||
|
toneMapping: this.#toneMapping,
|
||||||
|
alphaMode: this.#alphaMode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {WebGPUContextBuilder}
|
||||||
|
*/
|
||||||
|
apply() {
|
||||||
|
return this.#contextBuilder.configureCanvas(this.build())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @typedef {'core' | 'compatibility'} GPUFeatureLevel */
|
||||||
|
|
||||||
|
class GPUAdapterOptionsBuilder {
|
||||||
|
#contextBuilder
|
||||||
|
/** @type {GPUFeatureLevel} */
|
||||||
|
#featureLevel = 'core'
|
||||||
|
/** @type {GPUPowerPreference} */
|
||||||
|
#powerPreference
|
||||||
|
/** @type {boolean} */
|
||||||
|
#forceFallbackAdapter = false
|
||||||
|
/** @type {boolean} */
|
||||||
|
#xrCompatible = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {WebGPUContextBuilder} builder
|
||||||
|
*/
|
||||||
|
constructor(builder) {
|
||||||
|
this.#contextBuilder = builder
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {GPUFeatureLevel} featureLevel
|
||||||
|
*/
|
||||||
|
featureLevel(featureLevel) {
|
||||||
|
this.#featureLevel = featureLevel
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {GPUPowerPreference?} preference
|
||||||
|
*/
|
||||||
|
powerPreference(preference) {
|
||||||
|
this.#powerPreference = preference
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {boolean} force
|
||||||
|
*/
|
||||||
|
forceFallbackAdapter(force) {
|
||||||
|
this.#forceFallbackAdapter = force
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {boolean} compatible
|
||||||
|
*/
|
||||||
|
xrCompatible(compatible) {
|
||||||
|
this.#xrCompatible = compatible
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {GPURequestAdapterOptions}
|
||||||
|
*/
|
||||||
|
build() {
|
||||||
|
return {
|
||||||
|
featureLevel: this.#featureLevel,
|
||||||
|
powerPreference: this.#powerPreference,
|
||||||
|
forceFallbackAdapter: this.#forceFallbackAdapter,
|
||||||
|
xrCompatible: this.#xrCompatible
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply() {
|
||||||
|
return this.#contextBuilder.adapter(this.build())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GPUDeviceDescriptorBuilder {
|
||||||
|
/** @type {WebGPUContextBuilder} */
|
||||||
|
#contextBuilder
|
||||||
|
|
||||||
|
/** @type {GPUFeatureName[]} */
|
||||||
|
#features = []
|
||||||
|
|
||||||
|
/** @type {Record<string, GPUSize64?>} */
|
||||||
|
#limits = {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {WebGPUContextBuilder} builder
|
||||||
|
*/
|
||||||
|
constructor(builder) {
|
||||||
|
this.#contextBuilder = builder
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {GPUFeatureName[]} features
|
||||||
|
*/
|
||||||
|
requiredFeatures(features) {
|
||||||
|
this.#features = features
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {GPUFeatureName} feature
|
||||||
|
*/
|
||||||
|
addRequiredFeature(feature) {
|
||||||
|
this.#features.push(feature)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Record<string, GPUSize64?>} limits
|
||||||
|
*/
|
||||||
|
requiredLimits(limits) {
|
||||||
|
this.#limits = limits
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} key
|
||||||
|
* @param {GPUSize64?} value
|
||||||
|
*/
|
||||||
|
setRequiredLimit(key, value) {
|
||||||
|
this.#limits[key] = value
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {GPUDeviceDescriptor}
|
||||||
|
*/
|
||||||
|
build() {
|
||||||
|
return {
|
||||||
|
requiredFeatures: this.#features,
|
||||||
|
requiredLimits: this.#limits
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply() {
|
||||||
|
return this.#contextBuilder.device(this.build())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} GPUCanvasBuilderConfiguration
|
||||||
|
* @property {GPUTextureUsageFlags} [usage=0x10]
|
||||||
|
* @property {GPUTextureFormat[]} [viewFormats=[]]
|
||||||
|
* @property {PredefinedColorSpace} [colorSpace='srgb']
|
||||||
|
* @property {GPUCanvasToneMapping} [toneMapping = {}]
|
||||||
|
* @property {GPUCanvasAlphaMode} [alphaMode='opaque']
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class WebGPUContextBuilder {
|
||||||
|
/** @type {Promise<GPUAdapter>} */
|
||||||
|
#adapter
|
||||||
|
|
||||||
|
/** @type {GPUCanvasBuilderConfiguration} */
|
||||||
|
#canvasConfig
|
||||||
|
|
||||||
|
/** @type {GPUCanvasContext} */
|
||||||
|
#context
|
||||||
|
|
||||||
|
/** @type {Promise<GPUDevice>} */
|
||||||
|
#device
|
||||||
|
|
||||||
|
/** @type {number} */
|
||||||
|
#dpr = window.devicePixelRatio || 1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} type
|
||||||
|
*/
|
||||||
|
#warnDefault(type) {
|
||||||
|
console.warn(`WARN: Requesting WebGPU ${type} with default options.`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {GPURequestAdapterOptions} [descriptor]
|
||||||
|
* @returns {WebGPUContextBuilder}
|
||||||
|
* @throws {WebGPUError} Throws if WebGPU is not supported
|
||||||
|
*/
|
||||||
|
adapter(descriptor) {
|
||||||
|
if (!navigator.gpu) {
|
||||||
|
throw WebGPUError.unsupported()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#adapter = navigator.gpu.requestAdapter(descriptor)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
buildAdapterConfiguration() {
|
||||||
|
return new GPUAdapterOptionsBuilder(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {HTMLCanvasElement} canvas
|
||||||
|
* @returns {WebGPUContextBuilder}
|
||||||
|
*/
|
||||||
|
context(canvas) {
|
||||||
|
this.#context = canvas.getContext('webgpu')
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
buildCanvasConfiguration() {
|
||||||
|
return new GPUCanvasConfigurationBuilder(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {GPUDeviceDescriptor} [descriptor]
|
||||||
|
* @returns {WebGPUContextBuilder}
|
||||||
|
* @throws {WebGPUError} Throws if WebGPU is not supported
|
||||||
|
*/
|
||||||
|
device(descriptor) {
|
||||||
|
if (!this.#adapter) {
|
||||||
|
this.#warnDefault('adapter')
|
||||||
|
this.adapter()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#device = this.#adapter.then(adapter => adapter.requestDevice(descriptor))
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
buildDeviceDescriptor() {
|
||||||
|
return new GPUDeviceDescriptorBuilder(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {GPUCanvasBuilderConfiguration} [configuration]
|
||||||
|
* @returns {WebGPUContextBuilder}
|
||||||
|
*/
|
||||||
|
configureCanvas(configuration) {
|
||||||
|
this.#canvasConfig = configuration
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {Promise<WebGPUContext>}
|
||||||
|
* @throws {WebGPUBuilderError} Throws if context is undefined or if WebGPU is unsupported
|
||||||
|
*/
|
||||||
|
async build() {
|
||||||
|
if (!this.#context) {
|
||||||
|
throw WebGPUBuilderError.missing('context')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.#device) {
|
||||||
|
this.#warnDefault('device')
|
||||||
|
this.device()
|
||||||
|
}
|
||||||
|
|
||||||
|
const [adapter, device] = await Promise.all([this.#adapter, this.#device])
|
||||||
|
|
||||||
|
const format = navigator.gpu.getPreferredCanvasFormat()
|
||||||
|
|
||||||
|
if (this.#canvasConfig) {
|
||||||
|
this.#context.configure({
|
||||||
|
device,
|
||||||
|
format,
|
||||||
|
...this.#canvasConfig
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return new WebGPUContext(
|
||||||
|
this.#context,
|
||||||
|
adapter,
|
||||||
|
device,
|
||||||
|
format,
|
||||||
|
this.#dpr
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class WebGPUContext {
|
||||||
|
#adapter
|
||||||
|
#context
|
||||||
|
#device
|
||||||
|
#format
|
||||||
|
#dpr
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {GPUCanvasContext} context
|
||||||
|
* @param {GPUAdapter} adapter
|
||||||
|
* @param {GPUDevice} device
|
||||||
|
* @param {GPUTextureFormat} format
|
||||||
|
* @param {number} [dpr]
|
||||||
|
*/
|
||||||
|
constructor(context, adapter, device, format, dpr) {
|
||||||
|
this.#context = context
|
||||||
|
this.#adapter = adapter
|
||||||
|
this.#device = device
|
||||||
|
this.#format = format
|
||||||
|
this.#dpr = dpr || window.devicePixelRatio || 1
|
||||||
|
}
|
||||||
|
|
||||||
|
get adapter() { return this.#adapter }
|
||||||
|
get context() { return this.#context }
|
||||||
|
get device() { return this.#device }
|
||||||
|
get queue() { return this.#device.queue }
|
||||||
|
get format() { return this.#format }
|
||||||
|
get devicePixelRatio() { return this.#dpr }
|
||||||
|
|
||||||
|
static create() {
|
||||||
|
return new WebGPUContextBuilder()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
// https://www.w3.org/TR/webgpu/#enumdef-gpuvertexformat
|
// https://www.w3.org/TR/webgpu/#enumdef-gpuvertexformat
|
||||||
|
|
||||||
import { Union } from '/public/vendor/kojima/union.js'
|
import { capitalizen } from "/src/utils/index"
|
||||||
import { capitalizen } from '/src/utils/index'
|
|
||||||
|
|
||||||
|
// TODO: move this somewhere that makes sense
|
||||||
/** @typedef {Uint8ArrayConstructor | Int8ArrayConstructor | Uint16ArrayConstructor | Int16ArrayConstructor | Uint32ArrayConstructor | Int32ArrayConstructor | Float32ArrayConstructor | Float64ArrayConstructor} TypedArrayConstructor */
|
/** @typedef {Uint8ArrayConstructor | Int8ArrayConstructor | Uint16ArrayConstructor | Int16ArrayConstructor | Uint32ArrayConstructor | Int32ArrayConstructor | Float32ArrayConstructor | Float64ArrayConstructor} TypedArrayConstructor */
|
||||||
|
|
||||||
/** @type {GPUVertexFormat[]} */
|
/** @type {GPUVertexFormat[]} */
|
|
@ -1,427 +0,0 @@
|
||||||
export class WebGPUError extends Error {
|
|
||||||
/**
|
|
||||||
* @param {string} message
|
|
||||||
*/
|
|
||||||
constructor(message) {
|
|
||||||
super(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsupported() {
|
|
||||||
return new WebGPUError("WebGPU is unsupported in this browser")
|
|
||||||
}
|
|
||||||
|
|
||||||
static adapterUnavailable() {
|
|
||||||
return new WebGPUError("Could not request a WebGPU adapter.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class WebGPUBuilderError extends Error {
|
|
||||||
/**
|
|
||||||
* @param {string} message
|
|
||||||
*/
|
|
||||||
constructor(message) {
|
|
||||||
super(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} property
|
|
||||||
*/
|
|
||||||
static missing(property) {
|
|
||||||
return new WebGPUBuilderError(`Missing required property: ${property}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GPUCanvasConfigurationBuilder {
|
|
||||||
/** @type {WebGPUContextBuilder} */
|
|
||||||
#contextBuilder
|
|
||||||
/** @type {GPUTextureUsageFlags} */
|
|
||||||
#usage = 0x10
|
|
||||||
/** @type {GPUTextureFormat[]} */
|
|
||||||
#viewFormats = []
|
|
||||||
/** @type {PredefinedColorSpace} */
|
|
||||||
#colorSpace = 'srgb'
|
|
||||||
/** @type {GPUCanvasToneMapping} */
|
|
||||||
#toneMapping = {}
|
|
||||||
/** @type {GPUCanvasAlphaMode} */
|
|
||||||
#alphaMode = 'opaque'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {WebGPUContextBuilder} builder
|
|
||||||
*/
|
|
||||||
constructor(builder) {
|
|
||||||
this.#contextBuilder = builder
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {GPUTextureUsageFlags} usage
|
|
||||||
*/
|
|
||||||
usage(usage) {
|
|
||||||
this.#usage = usage
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {GPUTextureUsageFlags} usage
|
|
||||||
*/
|
|
||||||
addUsage(usage) {
|
|
||||||
this.#usage = this.#usage | usage
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {GPUTextureFormat[]} formats
|
|
||||||
*/
|
|
||||||
viewFormats(formats) {
|
|
||||||
this.#viewFormats = formats
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {GPUTextureFormat} format
|
|
||||||
*/
|
|
||||||
addViewFormat(format) {
|
|
||||||
this.#viewFormats.push(format)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {PredefinedColorSpace} space
|
|
||||||
*/
|
|
||||||
colorSpace(space) {
|
|
||||||
this.#colorSpace = space
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {GPUCanvasToneMappingMode} mode
|
|
||||||
*/
|
|
||||||
toneMappingMode(mode) {
|
|
||||||
this.#toneMapping = { mode }
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {GPUCanvasAlphaMode} mode
|
|
||||||
*/
|
|
||||||
alphaMode(mode) {
|
|
||||||
this.#alphaMode = mode
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {GPUCanvasBuilderConfiguration}
|
|
||||||
*/
|
|
||||||
build() {
|
|
||||||
return {
|
|
||||||
usage: this.#usage,
|
|
||||||
viewFormats: this.#viewFormats,
|
|
||||||
colorSpace: this.#colorSpace,
|
|
||||||
toneMapping: this.#toneMapping,
|
|
||||||
alphaMode: this.#alphaMode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {WebGPUContextBuilder}
|
|
||||||
*/
|
|
||||||
apply() {
|
|
||||||
return this.#contextBuilder.configureCanvas(this.build())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @typedef {'core' | 'compatibility'} GPUFeatureLevel */
|
|
||||||
|
|
||||||
class GPUAdapterOptionsBuilder {
|
|
||||||
#contextBuilder
|
|
||||||
/** @type {GPUFeatureLevel} */
|
|
||||||
#featureLevel = 'core'
|
|
||||||
/** @type {GPUPowerPreference} */
|
|
||||||
#powerPreference
|
|
||||||
/** @type {boolean} */
|
|
||||||
#forceFallbackAdapter = false
|
|
||||||
/** @type {boolean} */
|
|
||||||
#xrCompatible = false
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {WebGPUContextBuilder} builder
|
|
||||||
*/
|
|
||||||
constructor(builder) {
|
|
||||||
this.#contextBuilder = builder
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {GPUFeatureLevel} featureLevel
|
|
||||||
*/
|
|
||||||
featureLevel(featureLevel) {
|
|
||||||
this.#featureLevel = featureLevel
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {GPUPowerPreference?} preference
|
|
||||||
*/
|
|
||||||
powerPreference(preference) {
|
|
||||||
this.#powerPreference = preference
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {boolean} force
|
|
||||||
*/
|
|
||||||
forceFallbackAdapter(force) {
|
|
||||||
this.#forceFallbackAdapter = force
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {boolean} compatible
|
|
||||||
*/
|
|
||||||
xrCompatible(compatible) {
|
|
||||||
this.#xrCompatible = compatible
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {GPURequestAdapterOptions}
|
|
||||||
*/
|
|
||||||
build() {
|
|
||||||
return {
|
|
||||||
featureLevel: this.#featureLevel,
|
|
||||||
powerPreference: this.#powerPreference,
|
|
||||||
forceFallbackAdapter: this.#forceFallbackAdapter,
|
|
||||||
xrCompatible: this.#xrCompatible
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
apply() {
|
|
||||||
return this.#contextBuilder.adapter(this.build())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GPUDeviceDescriptorBuilder {
|
|
||||||
/** @type {WebGPUContextBuilder} */
|
|
||||||
#contextBuilder
|
|
||||||
|
|
||||||
/** @type {GPUFeatureName[]} */
|
|
||||||
#features = []
|
|
||||||
|
|
||||||
/** @type {Record<string, GPUSize64?>} */
|
|
||||||
#limits = {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {WebGPUContextBuilder} builder
|
|
||||||
*/
|
|
||||||
constructor(builder) {
|
|
||||||
this.#contextBuilder = builder
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {GPUFeatureName[]} features
|
|
||||||
*/
|
|
||||||
requiredFeatures(features) {
|
|
||||||
this.#features = features
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {GPUFeatureName} feature
|
|
||||||
*/
|
|
||||||
addRequiredFeature(feature) {
|
|
||||||
this.#features.push(feature)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Record<string, GPUSize64?>} limits
|
|
||||||
*/
|
|
||||||
requiredLimits(limits) {
|
|
||||||
this.#limits = limits
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} key
|
|
||||||
* @param {GPUSize64?} value
|
|
||||||
*/
|
|
||||||
setRequiredLimit(key, value) {
|
|
||||||
this.#limits[key] = value
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {GPUDeviceDescriptor}
|
|
||||||
*/
|
|
||||||
build() {
|
|
||||||
return {
|
|
||||||
requiredFeatures: this.#features,
|
|
||||||
requiredLimits: this.#limits
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
apply() {
|
|
||||||
return this.#contextBuilder.device(this.build())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {Object} GPUCanvasBuilderConfiguration
|
|
||||||
* @property {GPUTextureUsageFlags} [usage=0x10]
|
|
||||||
* @property {GPUTextureFormat[]} [viewFormats=[]]
|
|
||||||
* @property {PredefinedColorSpace} [colorSpace='srgb']
|
|
||||||
* @property {GPUCanvasToneMapping} [toneMapping = {}]
|
|
||||||
* @property {GPUCanvasAlphaMode} [alphaMode='opaque']
|
|
||||||
*/
|
|
||||||
|
|
||||||
export class WebGPUContextBuilder {
|
|
||||||
/** @type {Promise<GPUAdapter>} */
|
|
||||||
#adapter
|
|
||||||
|
|
||||||
/** @type {GPUCanvasBuilderConfiguration} */
|
|
||||||
#canvasConfig
|
|
||||||
|
|
||||||
/** @type {GPUCanvasContext} */
|
|
||||||
#context
|
|
||||||
|
|
||||||
/** @type {Promise<GPUDevice>} */
|
|
||||||
#device
|
|
||||||
|
|
||||||
/** @type {number} */
|
|
||||||
#dpr = window.devicePixelRatio || 1
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} type
|
|
||||||
*/
|
|
||||||
#warnDefault(type) {
|
|
||||||
console.warn(`WARN: Requesting WebGPU ${type} with default options.`)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {GPURequestAdapterOptions} [descriptor]
|
|
||||||
* @returns {WebGPUContextBuilder}
|
|
||||||
* @throws {WebGPUError} Throws if WebGPU is not supported
|
|
||||||
*/
|
|
||||||
adapter(descriptor) {
|
|
||||||
if (!navigator.gpu) {
|
|
||||||
throw WebGPUError.unsupported()
|
|
||||||
}
|
|
||||||
|
|
||||||
this.#adapter = navigator.gpu.requestAdapter(descriptor)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
buildAdapterConfiguration() {
|
|
||||||
return new GPUAdapterOptionsBuilder(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {HTMLCanvasElement} canvas
|
|
||||||
* @returns {WebGPUContextBuilder}
|
|
||||||
*/
|
|
||||||
context(canvas) {
|
|
||||||
this.#context = canvas.getContext('webgpu')
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
buildCanvasConfiguration() {
|
|
||||||
return new GPUCanvasConfigurationBuilder(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {GPUDeviceDescriptor} [descriptor]
|
|
||||||
* @returns {WebGPUContextBuilder}
|
|
||||||
* @throws {WebGPUError} Throws if WebGPU is not supported
|
|
||||||
*/
|
|
||||||
device(descriptor) {
|
|
||||||
if (!this.#adapter) {
|
|
||||||
this.#warnDefault('adapter')
|
|
||||||
this.adapter()
|
|
||||||
}
|
|
||||||
|
|
||||||
this.#device = this.#adapter.then(adapter => adapter.requestDevice(descriptor))
|
|
||||||
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
buildDeviceDescriptor() {
|
|
||||||
return new GPUDeviceDescriptorBuilder(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {GPUCanvasBuilderConfiguration} [configuration]
|
|
||||||
* @returns {WebGPUContextBuilder}
|
|
||||||
*/
|
|
||||||
configureCanvas(configuration) {
|
|
||||||
this.#canvasConfig = configuration
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {Promise<WebGPUContext>}
|
|
||||||
* @throws {WebGPUBuilderError} Throws if context is undefined or if WebGPU is unsupported
|
|
||||||
*/
|
|
||||||
async build() {
|
|
||||||
if (!this.#context) {
|
|
||||||
throw WebGPUBuilderError.missing('context')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.#device) {
|
|
||||||
this.#warnDefault('device')
|
|
||||||
this.device()
|
|
||||||
}
|
|
||||||
|
|
||||||
const [adapter, device] = await Promise.all([this.#adapter, this.#device])
|
|
||||||
|
|
||||||
const format = navigator.gpu.getPreferredCanvasFormat()
|
|
||||||
|
|
||||||
if (this.#canvasConfig) {
|
|
||||||
this.#context.configure({
|
|
||||||
device,
|
|
||||||
format,
|
|
||||||
...this.#canvasConfig
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return new WebGPUContext(
|
|
||||||
this.#context,
|
|
||||||
adapter,
|
|
||||||
device,
|
|
||||||
format,
|
|
||||||
this.#dpr
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class WebGPUContext {
|
|
||||||
#adapter
|
|
||||||
#context
|
|
||||||
#device
|
|
||||||
#format
|
|
||||||
#dpr
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {GPUCanvasContext} context
|
|
||||||
* @param {GPUAdapter} adapter
|
|
||||||
* @param {GPUDevice} device
|
|
||||||
* @param {GPUTextureFormat} format
|
|
||||||
* @param {number} [dpr]
|
|
||||||
*/
|
|
||||||
constructor(context, adapter, device, format, dpr) {
|
|
||||||
this.#context = context
|
|
||||||
this.#adapter = adapter
|
|
||||||
this.#device = device
|
|
||||||
this.#format = format
|
|
||||||
this.#dpr = dpr || window.devicePixelRatio || 1
|
|
||||||
}
|
|
||||||
|
|
||||||
get adapter() { return this.#adapter }
|
|
||||||
get context() { return this.#context }
|
|
||||||
get device() { return this.#device }
|
|
||||||
get queue() { return this.#device.queue }
|
|
||||||
get format() { return this.#format }
|
|
||||||
get devicePixelRatio() { return this.#dpr }
|
|
||||||
|
|
||||||
static create() {
|
|
||||||
return new WebGPUContextBuilder()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,105 +0,0 @@
|
||||||
/** @typedef {-1 | 0 | 1} Ternary */
|
|
||||||
/**
|
|
||||||
* @template T
|
|
||||||
* @typedef {(a: T, b: T) => Ternary | NaN} Comparer
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @template X, Y
|
|
||||||
* @param {X | Y} a
|
|
||||||
* @param {Y} b
|
|
||||||
* @returns [X | undefined, Y | undefined]
|
|
||||||
*/
|
|
||||||
const optionalSecond = (a, b) => {
|
|
||||||
let x = a
|
|
||||||
let y = b
|
|
||||||
|
|
||||||
if (!b) {
|
|
||||||
x = undefined
|
|
||||||
y = b
|
|
||||||
}
|
|
||||||
|
|
||||||
return [x, y]
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @template K, V
|
|
||||||
*/
|
|
||||||
export class OrderedMap extends Map {
|
|
||||||
_sort
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @overload
|
|
||||||
* @param {Iterable.<[K, V]>} iterable
|
|
||||||
* @param {Comparer<[K, V]>} comparer
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @overload
|
|
||||||
* @param {Comparer<[K, V]>} comparer
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @param {Iterable.<[K, V]> | Comparer<[K, V]>} iterable
|
|
||||||
* @param {Comparer<[K, V]>} [comparer]
|
|
||||||
*/
|
|
||||||
constructor(iterable, comparer) {
|
|
||||||
const [iter, sort] = optionalSecond(iterable, comparer)
|
|
||||||
|
|
||||||
/** @ts-ignore */
|
|
||||||
super(iter)
|
|
||||||
this._sort = sort
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Comparer<[K, V]>} comparer
|
|
||||||
*/
|
|
||||||
sortWith(comparer) {
|
|
||||||
this._sort = comparer
|
|
||||||
}
|
|
||||||
|
|
||||||
*[Symbol.iterator]() {
|
|
||||||
/** @ts-ignore */
|
|
||||||
yield* [...this.entries()].sort(this._sort)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @template T
|
|
||||||
* @param {(value: T) => number} get
|
|
||||||
* @returns {Comparer<T>}
|
|
||||||
*/
|
|
||||||
const prioritySort = get => (a, b) => get(a) - get(b)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @template K, V
|
|
||||||
*/
|
|
||||||
export class PriorityMap extends OrderedMap {
|
|
||||||
/**
|
|
||||||
* @typedef {(value: [K, V]) => number} PriorityGetter
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @type PriorityGetter */
|
|
||||||
_getter
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @overload
|
|
||||||
* @param {Iterable.<[K, V]>} iterable
|
|
||||||
* @param {PriorityGetter} getter
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @overload
|
|
||||||
* @param {PriorityGetter} getter
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @param {Iterable.<[K, V]> | PriorityGetter} iterable
|
|
||||||
* @param {PriorityGetter} [getter]
|
|
||||||
*/
|
|
||||||
constructor(iterable, getter) {
|
|
||||||
const [iter, get] = optionalSecond(iterable, getter)
|
|
||||||
|
|
||||||
/** @ts-ignore */
|
|
||||||
super(iter, prioritySort(get))
|
|
||||||
|
|
||||||
/** @ts-ignore */
|
|
||||||
this._getter = get
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
export class Attribute {
|
|
||||||
#name
|
|
||||||
#format
|
|
||||||
#location
|
|
||||||
#buffer
|
|
||||||
#count
|
|
||||||
#components
|
|
||||||
}
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue