custom build script; ambient wgsl declaration
This commit is contained in:
parent
95b25c962a
commit
81f2d118a0
9 changed files with 227 additions and 358 deletions
11
build.js
Normal file
11
build.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
import { build } from 'esbuild'
|
||||
import { wgsl } from 'esbuild-plugin-wgsl'
|
||||
|
||||
// @ts-ignore
|
||||
await build({
|
||||
entryPoints: ['index.js'],
|
||||
bundle: true,
|
||||
outfile: './dist/index.js',
|
||||
plugins: [wgsl({ filterWith: true, filterExtension: false })]
|
||||
})
|
||||
|
548
dist/index.js
vendored
548
dist/index.js
vendored
|
@ -59,16 +59,8 @@
|
|||
}
|
||||
};
|
||||
|
||||
// src/core/swap-chain.js
|
||||
// src/core/swap-chain.ts
|
||||
var SwapChain = class {
|
||||
_canvas;
|
||||
_device;
|
||||
_context;
|
||||
_format;
|
||||
_width;
|
||||
_height;
|
||||
/** @type {SwapChainConfiguration} */
|
||||
_configuration;
|
||||
get context() {
|
||||
return this._context;
|
||||
}
|
||||
|
@ -81,14 +73,6 @@
|
|||
get height() {
|
||||
return this._height;
|
||||
}
|
||||
/**
|
||||
* @param {HTMLCanvasElement} canvas
|
||||
* @param {GPUDevice} device
|
||||
* @param {GPUCanvasContext} [context]
|
||||
*
|
||||
* @throws {WebGPUError}
|
||||
* Throws an error if unable to request a WebGPU context
|
||||
*/
|
||||
constructor(canvas, device, context) {
|
||||
this._canvas = canvas;
|
||||
this._device = device;
|
||||
|
@ -100,9 +84,6 @@
|
|||
this._width = canvas.width;
|
||||
this._height = canvas.height;
|
||||
}
|
||||
/**
|
||||
* @param {SwapChainConfiguration} [configuration]
|
||||
*/
|
||||
configure(configuration) {
|
||||
if (configuration) {
|
||||
this._configuration = configuration;
|
||||
|
@ -119,11 +100,6 @@
|
|||
getCurrentTextureView() {
|
||||
return this._context.getCurrentTexture().createView();
|
||||
}
|
||||
/**
|
||||
* @template {number} const T
|
||||
* @param {PositiveInteger<T>} width
|
||||
* @param {PositiveInteger<T>} height
|
||||
*/
|
||||
resize(width, height) {
|
||||
if (width <= 0 || height <= 0) {
|
||||
return;
|
||||
|
@ -195,13 +171,13 @@
|
|||
ExternalTexture: 3
|
||||
});
|
||||
|
||||
// src/resources/buffer.js
|
||||
// src/resources/buffer.ts
|
||||
var Buffer = class _Buffer {
|
||||
_device;
|
||||
_handle;
|
||||
_mapped = false;
|
||||
/** @type {GPUBuffer} */
|
||||
_defaultStagingBuffer;
|
||||
constructor(device, texture) {
|
||||
this._mapped = false;
|
||||
this._device = device;
|
||||
this._handle = texture;
|
||||
}
|
||||
get handle() {
|
||||
return this._handle;
|
||||
}
|
||||
|
@ -214,18 +190,6 @@
|
|||
get resourceType() {
|
||||
return ResourceType.Buffer;
|
||||
}
|
||||
/**
|
||||
* @param {GPUDevice} device
|
||||
* @param {GPUBuffer} texture
|
||||
*/
|
||||
constructor(device, texture) {
|
||||
this._device = device;
|
||||
this._handle = texture;
|
||||
}
|
||||
/**
|
||||
* @param {GPUDevice} device
|
||||
* @param {GPUBufferDescriptor} descriptor
|
||||
*/
|
||||
static create(device, descriptor) {
|
||||
try {
|
||||
return new _Buffer(
|
||||
|
@ -236,26 +200,15 @@
|
|||
throw BufferError.from(err);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param {number} [size]
|
||||
*/
|
||||
_createStagingOptions(size = this.size) {
|
||||
return {
|
||||
size,
|
||||
usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST
|
||||
};
|
||||
}
|
||||
/**
|
||||
* @param {number} [size]
|
||||
*/
|
||||
_getStagingBuffer(size) {
|
||||
return this._device.createBuffer(this._createStagingOptions(size));
|
||||
}
|
||||
/**
|
||||
* @param {ArrayBufferView | ArrayBuffer} data
|
||||
* @param {number} [offset=0]
|
||||
* @param {number} [dataOffset=0]
|
||||
*/
|
||||
write(data, offset = 0, dataOffset = 0) {
|
||||
if (!(this.usage & GPUBufferUsage.COPY_DST)) {
|
||||
console.warn("Buffer usage does not include COPY_DST. Buffer.write may fail.");
|
||||
|
@ -270,15 +223,6 @@
|
|||
dataOffset
|
||||
);
|
||||
}
|
||||
/**
|
||||
* @typedef {Exclude<TypedArray, BigInt64Array | BigUint64Array>} SmallTypedArray
|
||||
* @typedef {Exclude<TypedArrayConstructor, BigInt64ArrayConstructor | BigUint64ArrayConstructor>} SmallTypedArrayConstructor
|
||||
*/
|
||||
/**
|
||||
* @param {SmallTypedArray | DataView | undefined} [out]
|
||||
* @param {number} [byteOffset=0]
|
||||
* @param {number} [byteSize]
|
||||
*/
|
||||
async read(out, byteOffset = 0, byteSize = -1) {
|
||||
if (!this._device) {
|
||||
throw WebGPUError.deviceUnavailable();
|
||||
|
@ -315,10 +259,7 @@
|
|||
await this.handle.mapAsync(GPUMapMode.READ, byteOffset, byteSize);
|
||||
range = this.handle.getMappedRange(byteOffset, byteSize);
|
||||
if (out != null) {
|
||||
const SourceView = (
|
||||
/** @type {SmallTypedArrayConstructor} */
|
||||
out.constructor
|
||||
);
|
||||
const SourceView = out.constructor;
|
||||
const bytesPerElement = SourceView.BYTES_PER_ELEMENT;
|
||||
if (!bytesPerElement) {
|
||||
if (out instanceof DataView) {
|
||||
|
@ -353,11 +294,6 @@
|
|||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* @param {Object} [descriptor={}]
|
||||
* @param {number} [descriptor.offset=0]
|
||||
* @param {number} [descriptor.size]
|
||||
*/
|
||||
toBindingResource({ offset, size } = {}) {
|
||||
return {
|
||||
buffer: this._handle,
|
||||
|
@ -371,17 +307,9 @@
|
|||
}
|
||||
};
|
||||
var UniformBuffer = class extends Buffer {
|
||||
/**
|
||||
* @param {GPUDevice} device
|
||||
* @param {GPUBuffer} buffer
|
||||
*/
|
||||
constructor(device, buffer) {
|
||||
super(device, buffer);
|
||||
}
|
||||
/**
|
||||
* @param {GPUDevice} device
|
||||
* @param {Omit<GPUBufferDescriptor, 'usage'>} descriptor
|
||||
*/
|
||||
static create(device, descriptor) {
|
||||
const usage = GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST;
|
||||
return super.create(device, {
|
||||
|
@ -5695,7 +5623,7 @@
|
|||
var GroupBindingMap = class extends Map {
|
||||
};
|
||||
|
||||
// src/enum.js
|
||||
// src/enum.ts
|
||||
var AddressMode = Enum(
|
||||
"clamp-to-edge",
|
||||
"repeat",
|
||||
|
@ -6244,22 +6172,14 @@
|
|||
}
|
||||
};
|
||||
|
||||
// src/resources/shader-module.js
|
||||
// src/resources/shader-module.ts
|
||||
var ShaderModule = class _ShaderModule {
|
||||
_handle;
|
||||
_code;
|
||||
/** @type {WgslReflect | undefined} */
|
||||
_reflection;
|
||||
get handle() {
|
||||
return this._handle;
|
||||
}
|
||||
get label() {
|
||||
return this._handle.label;
|
||||
}
|
||||
/**
|
||||
* @param {GPUDevice} device
|
||||
* @param {GPUShaderModuleDescriptor} descriptor
|
||||
*/
|
||||
constructor(device, descriptor) {
|
||||
this._code = descriptor.code;
|
||||
try {
|
||||
|
@ -6268,10 +6188,6 @@
|
|||
throw WebGPUObjectError.from(err, _ShaderModule);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param {GPUDevice} device
|
||||
* @param {GPUShaderModuleDescriptor} descriptor
|
||||
*/
|
||||
static create(device, descriptor) {
|
||||
return new _ShaderModule(device, descriptor);
|
||||
}
|
||||
|
@ -6284,22 +6200,15 @@
|
|||
}
|
||||
};
|
||||
var ReflectedShader = class _ReflectedShader {
|
||||
static _reflectTypes = ["uniforms", "storage", "textures", "samplers"];
|
||||
_module;
|
||||
static {
|
||||
this._reflectTypes = ["uniforms", "storage", "textures", "samplers"];
|
||||
}
|
||||
get module() {
|
||||
return this._module;
|
||||
}
|
||||
/**
|
||||
* @param {ShaderModule} shader
|
||||
*/
|
||||
constructor(shader) {
|
||||
this._module = shader;
|
||||
}
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {number} group
|
||||
* @returns {VariableInfo | undefined}
|
||||
*/
|
||||
findVariableInfo(name, group) {
|
||||
const reflection = this.module.reflect();
|
||||
for (const type of _ReflectedShader._reflectTypes) {
|
||||
|
@ -6312,17 +6221,10 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param {string} stageName
|
||||
* @returns {string | undefined}
|
||||
*/
|
||||
getEntrypoint(stageName) {
|
||||
const entry = this.module.reflect().entry;
|
||||
return entry[stageName].length === 1 ? entry[stageName][0].name : void 0;
|
||||
}
|
||||
/**
|
||||
* @returns {GPUShaderStageFlags}
|
||||
*/
|
||||
getShaderStages() {
|
||||
const entry = this._module.reflect().entry;
|
||||
let stages = 0;
|
||||
|
@ -6331,16 +6233,9 @@
|
|||
stages |= entry.compute.length > 0 ? GPUShaderStage.COMPUTE : 0;
|
||||
return stages;
|
||||
}
|
||||
/**
|
||||
* @param {GPUShaderStageFlags} stages
|
||||
*/
|
||||
hasStage(stages) {
|
||||
return this.getShaderStages() & stages;
|
||||
}
|
||||
/**
|
||||
* @param {GPUShaderStageFlags} stages
|
||||
* @param {GroupBindingMap} [out=new GroupBindingMap()]
|
||||
*/
|
||||
getBindingsForStage(stages, out = new GroupBindingMap()) {
|
||||
const groups = this._module.reflect().getBindGroups();
|
||||
groups.forEach((bindings, groupIndex) => {
|
||||
|
@ -6358,17 +6253,9 @@
|
|||
});
|
||||
return out;
|
||||
}
|
||||
/**
|
||||
* @param {Map<any, any>} map
|
||||
* @returns {number[]}
|
||||
*/
|
||||
static _sortKeyIndices(map) {
|
||||
return Array.from(map.keys()).sort((a2, b2) => a2 - b2);
|
||||
}
|
||||
/**
|
||||
* @param {VariableInfo} _variableInfo
|
||||
* @returns {GPUBufferBindingLayout}
|
||||
*/
|
||||
static _parseUniform(_variableInfo) {
|
||||
return {
|
||||
type: BufferBindingType.Uniform,
|
||||
|
@ -6377,14 +6264,9 @@
|
|||
minBindingSize: 0
|
||||
};
|
||||
}
|
||||
/**
|
||||
* @param {VariableInfo} variableInfo
|
||||
* @returns {GPUBufferBindingLayout}
|
||||
*/
|
||||
static _parseStorage(variableInfo) {
|
||||
return {
|
||||
type: accessToBufferType(
|
||||
/** @type {WGSLAccess} */
|
||||
variableInfo.access
|
||||
),
|
||||
// TODO: infer these two properties
|
||||
|
@ -6392,10 +6274,6 @@
|
|||
minBindingSize: 0
|
||||
};
|
||||
}
|
||||
/**
|
||||
* @param {VariableInfo} variableInfo
|
||||
* @returns {GPUTextureBindingLayout}
|
||||
*/
|
||||
static _parseTexture(variableInfo) {
|
||||
const [type, sampledType] = parseTextureType(
|
||||
variableInfo.type.name
|
||||
|
@ -6406,37 +6284,23 @@
|
|||
multisampled: type.includes("multisampled")
|
||||
};
|
||||
}
|
||||
/**
|
||||
* @param {VariableInfo} variableInfo
|
||||
* @returns {GPUSamplerBindingLayout}
|
||||
*/
|
||||
static _parseSampler(variableInfo) {
|
||||
return {
|
||||
type: typeToSamplerBindingType(
|
||||
/** @type {WGSLSamplerType} */
|
||||
variableInfo.type.name
|
||||
)
|
||||
};
|
||||
}
|
||||
/**
|
||||
* @param {VariableInfo} variableInfo
|
||||
* @returns {GPUStorageTextureBindingLayout}
|
||||
*/
|
||||
static _parseStorageTexture(variableInfo) {
|
||||
const [type] = parseTextureType(variableInfo.type.name);
|
||||
return {
|
||||
access: accessToStorageTextureAccess(
|
||||
/** @type {WGSLAccess} */
|
||||
variableInfo.access
|
||||
),
|
||||
format: wgslToWgpuFormat(variableInfo.type.name),
|
||||
viewDimension: typeToViewDimension(type)
|
||||
};
|
||||
}
|
||||
/**
|
||||
* @param {VariableStageInfo} variableStageInfo
|
||||
* @returns {GPUBindGroupLayoutEntry}
|
||||
*/
|
||||
static _variableInfoToEntry(variableStageInfo) {
|
||||
const { stages: visibility, variableInfo } = variableStageInfo;
|
||||
switch (variableInfo.resourceType) {
|
||||
|
@ -6475,9 +6339,6 @@
|
|||
return;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param {GroupBindingMap} groupBindings
|
||||
*/
|
||||
static createBindGroupLayoutEntries(groupBindings) {
|
||||
const sortedGroupIndices = this._sortKeyIndices(groupBindings);
|
||||
return sortedGroupIndices.map((groupIndex) => {
|
||||
|
@ -6487,14 +6348,6 @@
|
|||
}
|
||||
};
|
||||
var ShaderPair = class _ShaderPair {
|
||||
/** @type {ReflectedShader} */
|
||||
_vertex;
|
||||
/** @type {ReflectedShader} */
|
||||
_fragment;
|
||||
/**
|
||||
* @param {ReflectedShader} vertex
|
||||
* @param {ReflectedShader} [fragment]
|
||||
*/
|
||||
constructor(vertex, fragment) {
|
||||
if (!vertex) {
|
||||
throw new Error("Missing vertex shader");
|
||||
|
@ -6515,18 +6368,11 @@
|
|||
throw new Error("Missing fragment shader.");
|
||||
}
|
||||
}
|
||||
/** @param {ShaderModule} shader */
|
||||
static fromUnifiedShader(shader) {
|
||||
return new _ShaderPair(
|
||||
new ReflectedShader(shader)
|
||||
);
|
||||
}
|
||||
/**
|
||||
* @param {{
|
||||
vertex: ShaderModule,
|
||||
fragment?: ShaderModule
|
||||
* }} value
|
||||
*/
|
||||
static fromPair(value) {
|
||||
const vert = new ReflectedShader(value.vertex);
|
||||
const frag = value.fragment && new ReflectedShader(value.fragment);
|
||||
|
@ -6549,10 +6395,6 @@
|
|||
this._createGroupBindings()
|
||||
);
|
||||
}
|
||||
/**
|
||||
* @param {FragmentStateDescriptor} descriptor
|
||||
* @returns {GPUFragmentState}
|
||||
*/
|
||||
_getFragmentState(descriptor) {
|
||||
return {
|
||||
module: this._fragment.module.handle,
|
||||
|
@ -6561,10 +6403,6 @@
|
|||
targets: descriptor.targets || []
|
||||
};
|
||||
}
|
||||
/**
|
||||
* @param {VertexStateDescriptor} descriptor
|
||||
* @returns {GPUVertexState}
|
||||
*/
|
||||
_getVertexState(descriptor) {
|
||||
return {
|
||||
module: this._vertex.module.handle,
|
||||
|
@ -6573,11 +6411,6 @@
|
|||
buffers: descriptor.buffers || []
|
||||
};
|
||||
}
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {number} group
|
||||
* @returns {VariableInfo | undefined}
|
||||
*/
|
||||
findVariableInfo(name, group) {
|
||||
let variableInfo = this._vertex.findVariableInfo(name, group);
|
||||
if (!variableInfo && this._fragment !== this._vertex) {
|
||||
|
@ -6585,10 +6418,6 @@
|
|||
}
|
||||
return variableInfo;
|
||||
}
|
||||
/**
|
||||
* @param {ShaderPairStateDescriptor} descriptor
|
||||
* @returns {Pick<GPURenderPipelineDescriptor, 'vertex' | 'fragment'>}
|
||||
*/
|
||||
getRenderPipelineStates(descriptor) {
|
||||
return {
|
||||
fragment: this._getFragmentState(descriptor.fragment),
|
||||
|
@ -6597,10 +6426,8 @@
|
|||
}
|
||||
};
|
||||
|
||||
// src/rendering/render-pipeline.js
|
||||
// src/rendering/render-pipeline.ts
|
||||
var RenderPipeline = class {
|
||||
_handle;
|
||||
_label;
|
||||
get handle() {
|
||||
return this._handle;
|
||||
}
|
||||
|
@ -6617,29 +6444,19 @@
|
|||
}
|
||||
};
|
||||
|
||||
// src/core/command-recorder.js
|
||||
// src/core/command-recorder.ts
|
||||
var CommandRecorder = class _CommandRecorder {
|
||||
static _defaultClearValue = { r: 0, g: 0, b: 0, a: 1 };
|
||||
_device;
|
||||
_swapChain;
|
||||
_label;
|
||||
_encoder;
|
||||
/** @type {GPURenderPassEncoder | undefined} */
|
||||
_passEncoder;
|
||||
/**
|
||||
* @param {GPUDevice} device
|
||||
* @param {SwapChain} swapChain
|
||||
* @param {string} [label]
|
||||
*/
|
||||
static {
|
||||
this._defaultClearValue = { r: 0, g: 0, b: 0, a: 1 };
|
||||
}
|
||||
get label() {
|
||||
return this._encoder.label;
|
||||
}
|
||||
constructor(device, swapChain, label) {
|
||||
this._device = device;
|
||||
this._swapChain = swapChain;
|
||||
this._label = label;
|
||||
this._encoder = device.createCommandEncoder({ label });
|
||||
}
|
||||
/**
|
||||
* @returns {[GPURenderPassColorAttachment]}
|
||||
*/
|
||||
_defaultColorAttachment() {
|
||||
const view = this._swapChain.getCurrentTextureView();
|
||||
return [{
|
||||
|
@ -6649,18 +6466,13 @@
|
|||
storeOp: StoreOp.Store
|
||||
}];
|
||||
}
|
||||
/**
|
||||
* @param {GPURenderPassColorAttachment[]} [colorAttachments]
|
||||
* @param {GPURenderPassDepthStencilAttachment} [depthStencilAttachment]
|
||||
* @returns {GPURenderPassEncoder}
|
||||
*/
|
||||
beginRenderPass(colorAttachments, depthStencilAttachment) {
|
||||
if (this._passEncoder) {
|
||||
throw CommandRecorderError.activeRenderPass();
|
||||
}
|
||||
const attachments = colorAttachments || this._defaultColorAttachment();
|
||||
const descriptor = {
|
||||
label: this._label || "RenderPass",
|
||||
label: this.label || "RenderPass",
|
||||
colorAttachments: attachments,
|
||||
depthStencilAttachment
|
||||
};
|
||||
|
@ -6682,10 +6494,8 @@
|
|||
}
|
||||
};
|
||||
|
||||
// src/resources/bind-group-layout.js
|
||||
// src/resources/bind-group-layout.ts
|
||||
var BindGroupLayout = class _BindGroupLayout {
|
||||
_device;
|
||||
_handle;
|
||||
get handle() {
|
||||
return this._handle;
|
||||
}
|
||||
|
@ -6716,10 +6526,8 @@
|
|||
}
|
||||
};
|
||||
|
||||
// src/resources/bind-group.js
|
||||
// src/resources/bind-group.ts
|
||||
var BindGroup = class _BindGroup {
|
||||
_device;
|
||||
_handle;
|
||||
get handle() {
|
||||
return this._handle;
|
||||
}
|
||||
|
@ -6772,13 +6580,11 @@
|
|||
}
|
||||
};
|
||||
|
||||
// src/resources/texture.js
|
||||
// src/resources/texture.ts
|
||||
var Texture = class _Texture {
|
||||
static _defaultUsage = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT;
|
||||
_device;
|
||||
_handle;
|
||||
/** @type {GPUTextureView | undefined} */
|
||||
_defaultView;
|
||||
static {
|
||||
this._defaultUsage = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT;
|
||||
}
|
||||
get handle() {
|
||||
return this._handle;
|
||||
}
|
||||
|
@ -6951,10 +6757,8 @@
|
|||
}
|
||||
};
|
||||
|
||||
// src/resources/sampler.js
|
||||
// src/resources/sampler.ts
|
||||
var Sampler = class _Sampler {
|
||||
_device;
|
||||
_handle;
|
||||
get handle() {
|
||||
return this._handle;
|
||||
}
|
||||
|
@ -6991,12 +6795,8 @@
|
|||
}
|
||||
};
|
||||
|
||||
// src/resources/material.js
|
||||
// src/resources/material.ts
|
||||
var Material = class _Material {
|
||||
_device;
|
||||
_shaders;
|
||||
_bindGroupLayouts;
|
||||
_pipelineLayout;
|
||||
get shaders() {
|
||||
return this._shaders;
|
||||
}
|
||||
|
@ -7101,81 +6901,52 @@
|
|||
}
|
||||
};
|
||||
|
||||
// src/core/graphics-device.js
|
||||
// src/core/graphics-device.ts
|
||||
var GraphicsDeviceBuilder = class {
|
||||
_canvas;
|
||||
get canvas() {
|
||||
return this._canvas;
|
||||
}
|
||||
/** @type {GPURequestAdapterOptions} */
|
||||
_adapter_options;
|
||||
/** @type {GPUDeviceDescriptor} */
|
||||
_device_descriptor;
|
||||
/**
|
||||
* @param {HTMLCanvasElement} [canvasElement]
|
||||
*/
|
||||
constructor(canvasElement) {
|
||||
this._canvas = canvasElement;
|
||||
}
|
||||
isSupported() {
|
||||
return navigator.gpu;
|
||||
}
|
||||
/**
|
||||
* @param {HTMLCanvasElement} canvasElement
|
||||
*/
|
||||
withCanvas(canvasElement) {
|
||||
this._canvas = canvasElement;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* @param {GPURequestAdapterOptions} [options]
|
||||
*/
|
||||
withAdapter(options2) {
|
||||
if (!this.isSupported()) {
|
||||
throw WebGPUError.unsupported();
|
||||
}
|
||||
this._adapter_options = options2;
|
||||
this._adapterOptions = options2;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* @param {GPUDeviceDescriptor} [options]
|
||||
*/
|
||||
withDevice(options2) {
|
||||
if (!this.isSupported()) {
|
||||
throw WebGPUError.unsupported();
|
||||
}
|
||||
this._device_descriptor = options2;
|
||||
this._deviceDescriptor = options2;
|
||||
return this;
|
||||
}
|
||||
async build() {
|
||||
return new GraphicsDevice(
|
||||
this._canvas,
|
||||
new DeviceHandler(
|
||||
this._adapter_options,
|
||||
this._device_descriptor
|
||||
this._adapterOptions,
|
||||
this._deviceDescriptor
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
var DeviceHandler = class {
|
||||
/** @type {GPURequestAdapterOptions} */
|
||||
_adapterOptions;
|
||||
/** @type {GPUAdapter} */
|
||||
_adapter;
|
||||
get adapter() {
|
||||
return this._adapter;
|
||||
}
|
||||
/** @type {GPUDeviceDescriptor} */
|
||||
_deviceDescriptor;
|
||||
/** @type {GPUDevice} */
|
||||
_device;
|
||||
get device() {
|
||||
return this._device;
|
||||
}
|
||||
/**
|
||||
* @param {GPURequestAdapterOptions} adapterOptions
|
||||
* @param {GPUDeviceDescriptor} deviceDescriptor
|
||||
*/
|
||||
constructor(adapterOptions, deviceDescriptor) {
|
||||
this._adapterOptions = adapterOptions;
|
||||
this._deviceDescriptor = deviceDescriptor;
|
||||
|
@ -7192,13 +6963,12 @@
|
|||
}
|
||||
};
|
||||
var GraphicsDevice = class extends EventEmitter {
|
||||
_canvas;
|
||||
_deviceHandler;
|
||||
/** @type {SwapChain} */
|
||||
_swapChain;
|
||||
/** @type {GPUQueue} */
|
||||
_queue;
|
||||
_isInitialized = false;
|
||||
constructor(canvas, deviceHandler) {
|
||||
super();
|
||||
this._isInitialized = false;
|
||||
this._canvas = canvas;
|
||||
this._deviceHandler = deviceHandler;
|
||||
}
|
||||
get isInitialized() {
|
||||
return this._isInitialized;
|
||||
}
|
||||
|
@ -7214,18 +6984,6 @@
|
|||
get swapChain() {
|
||||
return this._swapChain;
|
||||
}
|
||||
/**
|
||||
* @param {HTMLCanvasElement} canvas
|
||||
* @param {DeviceHandler} deviceHandler
|
||||
*/
|
||||
constructor(canvas, deviceHandler) {
|
||||
super();
|
||||
this._canvas = canvas;
|
||||
this._deviceHandler = deviceHandler;
|
||||
}
|
||||
/**
|
||||
* @param {HTMLCanvasElement} [canvas]
|
||||
*/
|
||||
static build(canvas) {
|
||||
return new GraphicsDeviceBuilder(canvas);
|
||||
}
|
||||
|
@ -7251,17 +7009,6 @@
|
|||
);
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* @typedef {Omit<GPUBufferDescriptor, 'mappedAtCreation'>} BufferDescriptor
|
||||
*/
|
||||
/**
|
||||
* Create a GPU buffer
|
||||
* @param {BufferDescriptor} descriptor
|
||||
* @param {ArrayBufferView | ArrayBuffer} [data]
|
||||
* @returns {Buffer}
|
||||
*
|
||||
* @throws {GPUBufferError}
|
||||
*/
|
||||
createBuffer(descriptor, data) {
|
||||
if (!this._isInitialized) {
|
||||
throw GraphicsDeviceError.uninitialized();
|
||||
|
@ -7276,11 +7023,6 @@
|
|||
throw BufferError.from(err);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param {number} size
|
||||
* @param {ArrayBufferView | ArrayBuffer} [data]
|
||||
* @param {string} [label]
|
||||
*/
|
||||
createUniformBuffer(size, data, label) {
|
||||
if (!this._isInitialized) {
|
||||
throw GraphicsDeviceError.uninitialized();
|
||||
|
@ -7294,12 +7036,6 @@
|
|||
}
|
||||
return buffer;
|
||||
}
|
||||
/**
|
||||
* Creates a shader module from WGSL code.
|
||||
* @param {string} code
|
||||
* @param {string} [label]
|
||||
* @returns {ShaderModule}
|
||||
*/
|
||||
createShaderModule(code, label) {
|
||||
if (!this._isInitialized) {
|
||||
throw GraphicsDeviceError.uninitialized();
|
||||
|
@ -7310,11 +7046,6 @@
|
|||
throw WebGPUObjectError.from(err, ShaderModule);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Creates a render pipeline.
|
||||
* @param {GPURenderPipelineDescriptor} descriptor - Raw render pipeline descriptor.
|
||||
* @returns {RenderPipeline}
|
||||
*/
|
||||
createRenderPipeline(descriptor) {
|
||||
if (!this._isInitialized) {
|
||||
throw GraphicsDeviceError.uninitialized();
|
||||
|
@ -7326,9 +7057,6 @@
|
|||
throw WebGPUObjectError.from(err, RenderPipeline);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param {import('../resources/material.js').ShaderPairDescriptor} shaders
|
||||
*/
|
||||
createMaterial(shaders) {
|
||||
if (!this._isInitialized) {
|
||||
throw GraphicsDeviceError.uninitialized();
|
||||
|
@ -7339,21 +7067,12 @@
|
|||
throw WebGPUObjectError.from(err, Material);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Creates a CommandRecorder to begin recording GPU commands.
|
||||
* @param {string} [label]
|
||||
* @returns {CommandRecorder}
|
||||
*/
|
||||
createCommandRecorder(label) {
|
||||
if (!this._isInitialized) {
|
||||
throw GraphicsDeviceError.uninitialized();
|
||||
}
|
||||
return new CommandRecorder(this.device, this._swapChain, label);
|
||||
}
|
||||
/**
|
||||
* @param {GPUBindGroupLayoutEntry[]} entries
|
||||
* @param {string} [label]
|
||||
*/
|
||||
createBindGroupLayout(entries, label) {
|
||||
if (!this._isInitialized) {
|
||||
throw GraphicsDeviceError.uninitialized();
|
||||
|
@ -7363,10 +7082,6 @@
|
|||
entries
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @param {BindGroupEntry} binding
|
||||
* @returns {GPUBindingResource}
|
||||
*/
|
||||
_getBindingResource(binding) {
|
||||
const resource = binding.resource;
|
||||
switch (resource.resourceType) {
|
||||
|
@ -7388,11 +7103,6 @@
|
|||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param {BindGroupLayout} layout
|
||||
* @param {BindGroupEntry[]} bindings
|
||||
* @param {string} [label]
|
||||
*/
|
||||
createBindGroup(layout, bindings, label) {
|
||||
if (!this._isInitialized) {
|
||||
throw GraphicsDeviceError.uninitialized();
|
||||
|
@ -7407,10 +7117,6 @@
|
|||
label
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @param {Array<BindGroupLayout>} layouts
|
||||
* @param {string} [label]
|
||||
*/
|
||||
createPipelineLayout(layouts, label) {
|
||||
if (!this._isInitialized) {
|
||||
throw GraphicsDeviceError.uninitialized();
|
||||
|
@ -7421,18 +7127,12 @@
|
|||
bindGroupLayouts
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @param {GPUSamplerDescriptor} [descriptor]
|
||||
*/
|
||||
createSampler(descriptor) {
|
||||
if (!this._isInitialized) {
|
||||
throw GraphicsDeviceError.uninitialized();
|
||||
}
|
||||
return Sampler.create(this.device, descriptor);
|
||||
}
|
||||
/**
|
||||
* @param {GPUTextureDescriptor} descriptor
|
||||
*/
|
||||
createTexture(descriptor) {
|
||||
if (!this._isInitialized) {
|
||||
throw GraphicsDeviceError.uninitialized();
|
||||
|
@ -7443,15 +7143,6 @@
|
|||
throw WebGPUObjectError.from(err, Texture);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param {ImageBitmap} bitmap
|
||||
* @param {object} [options]
|
||||
* @param {string} [options.label]
|
||||
* @param {GPUTextureFormat} [options.format='rgba8unorm']
|
||||
* @param {GPUTextureUsageFlags} [options.usage=TEXTURE_BINDING | COPY_DST | RENDER_ATTACHMENT]
|
||||
* @param {boolean} [options.generateMipmaps=false]
|
||||
* @param {boolean} [options.flipY=false]
|
||||
*/
|
||||
createTextureFromBitmap(bitmap, options2) {
|
||||
if (!this._isInitialized) {
|
||||
throw GraphicsDeviceError.uninitialized();
|
||||
|
@ -7487,10 +7178,6 @@
|
|||
throw WebGPUObjectError.from(err, Texture);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Submits an array of command buffers to the GPU queue.
|
||||
* @param {GPUCommandBuffer[]} commandBuffers
|
||||
*/
|
||||
submitCommands(commandBuffers) {
|
||||
if (!this._isInitialized || !commandBuffers || commandBuffers.length === 0) return;
|
||||
this.queue.submit(commandBuffers);
|
||||
|
@ -7509,6 +7196,158 @@
|
|||
}
|
||||
};
|
||||
|
||||
// src/utils/mip-shader.wgsl
|
||||
var mip_shader_default = "var<private> pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(\n vec2<f32>(-1.0, -1.0), vec2<f32>(-1.0, 3.0), vec2<f32>(3.0, -1.0));\n\nstruct VertexOutput {\n @builtin(position) position : vec4<f32>,\n @location(0) texCoord : vec2<f32>,\n};\n\n@vertex\nfn vertexMain(@builtin(vertex_index) vertexIndex : u32) -> VertexOutput {\n var output : VertexOutput;\n output.texCoord = pos[vertexIndex] * vec2<f32>(0.5, -0.5) + vec2<f32>(0.5);\n output.position = vec4<f32>(pos[vertexIndex], 0.0, 1.0);\n return output;\n}\n\n@group(0) @binding(0) var imgSampler : sampler;\n@group(0) @binding(1) var img : texture_2d<f32>;\n\n@fragment\nfn fragmentMain(@location(0) texCoord : vec2<f32>) -> @location(0) vec4<f32> {\n return textureSample(img, imgSampler, texCoord);\n}\n";
|
||||
|
||||
// src/utils/mip-generator.ts
|
||||
var mip = (n2) => Math.max(1, n2 >>> 1);
|
||||
var MipGenerator = class {
|
||||
constructor(device) {
|
||||
this._device = device;
|
||||
this._sampler = device.createSampler({ minFilter: "linear" });
|
||||
this._pipelines = {};
|
||||
}
|
||||
_getShader() {
|
||||
if (!this._shader) {
|
||||
this._shader = this._device.createShaderModule({
|
||||
code: mip_shader_default
|
||||
});
|
||||
}
|
||||
return this._shader;
|
||||
}
|
||||
_getBindGroupLayout() {
|
||||
if (!this._bindGroupLayout) {
|
||||
this._bindGroupLayout = this._device.createBindGroupLayout({
|
||||
entries: [{
|
||||
binding: 0,
|
||||
visibility: GPUShaderStage.FRAGMENT,
|
||||
sampler: {}
|
||||
}, {
|
||||
binding: 1,
|
||||
visibility: GPUShaderStage.FRAGMENT,
|
||||
texture: {}
|
||||
}]
|
||||
});
|
||||
}
|
||||
return this._bindGroupLayout;
|
||||
}
|
||||
_getPipelineLayout() {
|
||||
if (!this._pipelineLayout) {
|
||||
this._pipelineLayout = this._device.createPipelineLayout({
|
||||
label: "Mipmap Generator",
|
||||
bindGroupLayouts: [this._getBindGroupLayout()]
|
||||
});
|
||||
}
|
||||
return this._pipelineLayout;
|
||||
}
|
||||
getPipeline(format) {
|
||||
let pipeline = this._pipelines[format];
|
||||
if (!pipeline) {
|
||||
const shader = this._getShader();
|
||||
pipeline = this._device.createRenderPipeline({
|
||||
layout: this._getPipelineLayout(),
|
||||
vertex: {
|
||||
module: shader,
|
||||
entryPoint: "vs_main"
|
||||
},
|
||||
fragment: {
|
||||
module: shader,
|
||||
entryPoint: "fs_main",
|
||||
targets: [{ format }]
|
||||
}
|
||||
});
|
||||
this._pipelines[format] = pipeline;
|
||||
}
|
||||
return pipeline;
|
||||
}
|
||||
generateMipmap(texture, descriptor) {
|
||||
const pipeline = this.getPipeline(descriptor.format);
|
||||
if (descriptor.dimension !== "2d") {
|
||||
throw new Error("Generating mipmaps for anything except 2d is unsupported.");
|
||||
}
|
||||
let mipTexture = texture;
|
||||
const { width, height, depthOrArrayLayers } = descriptor.size;
|
||||
const renderToSource = descriptor.usage & GPUTextureUsage.RENDER_ATTACHMENT;
|
||||
if (!renderToSource) {
|
||||
mipTexture = this._device.createTexture({
|
||||
size: {
|
||||
width: mip(width),
|
||||
height: mip(height),
|
||||
depthOrArrayLayers
|
||||
},
|
||||
format: descriptor.format,
|
||||
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_SRC | GPUTextureUsage.RENDER_ATTACHMENT,
|
||||
mipLevelCount: descriptor.mipLevelCount - 1
|
||||
});
|
||||
}
|
||||
const encoder = this._device.createCommandEncoder({});
|
||||
for (let layer = 0; layer < depthOrArrayLayers; ++layer) {
|
||||
let srcView = texture.createView({
|
||||
baseMipLevel: 0,
|
||||
mipLevelCount: 1,
|
||||
dimension: "2d",
|
||||
baseArrayLayer: layer,
|
||||
arrayLayerCount: 1
|
||||
});
|
||||
let dstMipLevel = renderToSource ? 1 : 0;
|
||||
for (let i2 = 1; i2 < descriptor.mipLevelCount; ++i2) {
|
||||
const dstView = mipTexture.createView({
|
||||
baseMipLevel: dstMipLevel++,
|
||||
mipLevelCount: 1,
|
||||
dimension: "2d",
|
||||
baseArrayLayer: layer,
|
||||
arrayLayerCount: 1
|
||||
});
|
||||
const passEncoder = encoder.beginRenderPass({
|
||||
colorAttachments: [{
|
||||
view: dstView,
|
||||
loadOp: "clear",
|
||||
storeOp: "store"
|
||||
}]
|
||||
});
|
||||
const bindGroup = this._device.createBindGroup({
|
||||
layout: this._bindGroupLayout,
|
||||
entries: [{
|
||||
binding: 0,
|
||||
resource: this._sampler
|
||||
}, {
|
||||
binding: 1,
|
||||
resource: srcView
|
||||
}]
|
||||
});
|
||||
passEncoder.setPipeline(pipeline);
|
||||
passEncoder.setBindGroup(0, bindGroup);
|
||||
passEncoder.draw(3, 1, 0, 0);
|
||||
passEncoder.end();
|
||||
srcView = dstView;
|
||||
}
|
||||
}
|
||||
if (!renderToSource) {
|
||||
const mipLevelSize = {
|
||||
width: mip(width),
|
||||
height: mip(height),
|
||||
depthOrArrayLayers
|
||||
};
|
||||
for (let i2 = 1; i2 < descriptor.mipLevelCount; ++i2) {
|
||||
encoder.copyTextureToTexture({
|
||||
texture: mipTexture,
|
||||
mipLevel: i2 - 1
|
||||
}, {
|
||||
texture,
|
||||
mipLevel: i2
|
||||
}, mipLevelSize);
|
||||
mipLevelSize.width = mip(mipLevelSize.width);
|
||||
mipLevelSize.height = mip(mipLevelSize.height);
|
||||
}
|
||||
}
|
||||
this._device.queue.submit([encoder.finish()]);
|
||||
if (!renderToSource) {
|
||||
mipTexture.destroy();
|
||||
}
|
||||
return texture;
|
||||
}
|
||||
};
|
||||
|
||||
// index.js
|
||||
async function main() {
|
||||
const canvas = (
|
||||
|
@ -7522,6 +7361,7 @@
|
|||
canvas.width = 800;
|
||||
canvas.height = 600;
|
||||
const graphicsDevice = await GraphicsDevice.build().withCanvas(canvas).withAdapter({ powerPreference: PowerPreference.HighPerformance }).build();
|
||||
const mipGenerator = new MipGenerator(graphicsDevice.device);
|
||||
const success = await graphicsDevice.initialize();
|
||||
if (!success) {
|
||||
console.error("Failed to initialize WebGPU.");
|
||||
|
|
2
index.js
2
index.js
|
@ -1,5 +1,6 @@
|
|||
import { GraphicsDevice } from './src/core/graphics-device.js'
|
||||
import { PowerPreference, VertexFormat } from './src/enum.js'
|
||||
import { MipGenerator } from './src/utils/mip-generator.js'
|
||||
|
||||
async function main() {
|
||||
const canvas = /** @type {HTMLCanvasElement} */ (document.getElementById('webgpu-canvas'))
|
||||
|
@ -17,6 +18,7 @@ async function main() {
|
|||
.withCanvas(canvas)
|
||||
.withAdapter({ powerPreference: PowerPreference.HighPerformance })
|
||||
.build()
|
||||
const mipGenerator = new MipGenerator(graphicsDevice.device)
|
||||
|
||||
const success = await graphicsDevice.initialize()
|
||||
|
||||
|
|
10
package-lock.json
generated
10
package-lock.json
generated
|
@ -14,6 +14,7 @@
|
|||
"devDependencies": {
|
||||
"@webgpu/types": "^0.1.60",
|
||||
"esbuild": "^0.25.2",
|
||||
"esbuild-plugin-wgsl": "git+https://git.kitsu.cafe/rowan/esbuild-plugin-wgsl.git",
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
},
|
||||
|
@ -490,6 +491,15 @@
|
|||
"@esbuild/win32-x64": "0.25.2"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-plugin-wgsl": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "git+https://git.kitsu.cafe/rowan/esbuild-plugin-wgsl.git#e1ab30a11972e13b2ee29ddb1149e143ea686dae",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"esbuild": "0.x.x"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.8.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"type": "module",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "esbuild index.js --bundle --outfile=./dist/index.js",
|
||||
"build": "node build.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
|
@ -14,6 +14,7 @@
|
|||
"devDependencies": {
|
||||
"@webgpu/types": "^0.1.60",
|
||||
"esbuild": "^0.25.2",
|
||||
"esbuild-plugin-wgsl": "git+https://git.kitsu.cafe/rowan/esbuild-plugin-wgsl.git",
|
||||
"typescript": "^5.8.3"
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
|
@ -49,7 +49,7 @@ class GraphicsDeviceBuilder {
|
|||
}
|
||||
|
||||
|
||||
constructor(canvasElement: HTMLCanvasElement) {
|
||||
constructor(canvasElement?: HTMLCanvasElement) {
|
||||
this._canvas = canvasElement
|
||||
}
|
||||
|
||||
|
|
5
src/global.d.ts
vendored
Normal file
5
src/global.d.ts
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
declare module '*.wgsl' {
|
||||
const value: string
|
||||
export default value
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import code from './mip-shader.wgsl' with { type: 'text' }
|
||||
import code from './mip-shader.wgsl'
|
||||
|
||||
const mip = (n: number) => Math.max(1, n >>> 1)
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"module": "esnext",
|
||||
"module": "es2022",
|
||||
"moduleResolution": "node",
|
||||
"target": "es6",
|
||||
"lib": ["es2022", "dom"],
|
||||
|
|
Loading…
Add table
Reference in a new issue