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,
|
||||
"requires": true,
|
||||
"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
|
||||
|
||||
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 */
|
||||
|
||||
/** @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