cleanup
This commit is contained in:
parent
81f2d118a0
commit
0763a35a65
15 changed files with 136 additions and 448 deletions
2
build.js
2
build.js
|
@ -6,6 +6,6 @@ await build({
|
|||
entryPoints: ['index.js'],
|
||||
bundle: true,
|
||||
outfile: './dist/index.js',
|
||||
plugins: [wgsl({ filterWith: true, filterExtension: false })]
|
||||
plugins: [wgsl()]
|
||||
})
|
||||
|
||||
|
|
153
dist/index.js
vendored
153
dist/index.js
vendored
|
@ -7196,158 +7196,6 @@
|
|||
}
|
||||
};
|
||||
|
||||
// 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 = (
|
||||
|
@ -7361,7 +7209,6 @@
|
|||
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.");
|
||||
|
|
18
index.js
18
index.js
|
@ -1,6 +1,5 @@
|
|||
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'))
|
||||
|
@ -14,11 +13,9 @@ async function main() {
|
|||
canvas.height = 600
|
||||
|
||||
|
||||
const graphicsDevice = await GraphicsDevice.build()
|
||||
.withCanvas(canvas)
|
||||
const graphicsDevice = await GraphicsDevice.build(canvas)
|
||||
.withAdapter({ powerPreference: PowerPreference.HighPerformance })
|
||||
.build()
|
||||
const mipGenerator = new MipGenerator(graphicsDevice.device)
|
||||
.finish()
|
||||
|
||||
const success = await graphicsDevice.initialize()
|
||||
|
||||
|
@ -119,22 +116,11 @@ async function main() {
|
|||
|
||||
const pipeline = graphicsDevice.createRenderPipeline(pipelineDescriptor)
|
||||
|
||||
/** @type {Array<import('./src/core/graphics-device.js').BindGroupEntry>} */
|
||||
const uniformBindings = [{
|
||||
binding: 0,
|
||||
resource: uniformBuffer
|
||||
}]
|
||||
|
||||
const uniformBindGroup = material.createBindGroup(
|
||||
0,
|
||||
{ transform: uniformBuffer },
|
||||
'Uniforms'
|
||||
)
|
||||
//const uniformBindGroup = graphicsDevice.createBindGroup(
|
||||
// material.bindGroupLayouts[0],
|
||||
// uniformBindings,
|
||||
// 'Uniforms'
|
||||
//)
|
||||
|
||||
async function frame() {
|
||||
if (!graphicsDevice.isInitialized) {
|
||||
|
|
|
@ -35,7 +35,7 @@ export class CommandRecorder {
|
|||
}]
|
||||
}
|
||||
|
||||
beginRenderPass(colorAttachments: GPURenderPassColorAttachment[], depthStencilAttachment: GPURenderPassDepthStencilAttachment): GPURenderPassEncoder {
|
||||
beginRenderPass(colorAttachments?: GPURenderPassColorAttachment[], depthStencilAttachment?: GPURenderPassDepthStencilAttachment): GPURenderPassEncoder {
|
||||
if (this._passEncoder) {
|
||||
throw CommandRecorderError.activeRenderPass()
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Buffer, UniformBuffer } from '../resources/buffer.js'
|
|||
import { GraphicsDeviceError, WebGPUError, BufferError, WebGPUObjectError } from '../utils/errors.js'
|
||||
import { GraphicsDeviceInitialized, GraphicsDeviceLost } from '../utils/events.js'
|
||||
import { EventEmitter } from '../utils/index.js'
|
||||
import { ShaderModule, ShaderPairStateDescriptor } from '../resources/shader-module.js'
|
||||
import { ShaderModule, ShaderPairDescriptor } from '../resources/shader-module.js'
|
||||
import { RenderPipeline } from '../rendering/render-pipeline.js'
|
||||
import { CommandRecorder } from './command-recorder.js'
|
||||
import { BindGroupLayout } from '../resources/bind-group-layout.js'
|
||||
|
@ -80,7 +80,7 @@ class GraphicsDeviceBuilder {
|
|||
return this
|
||||
}
|
||||
|
||||
async build() {
|
||||
async finish() {
|
||||
return new GraphicsDevice(
|
||||
this._canvas,
|
||||
new DeviceHandler(
|
||||
|
@ -160,7 +160,7 @@ export class GraphicsDevice extends EventEmitter {
|
|||
this._deviceHandler = deviceHandler
|
||||
}
|
||||
|
||||
static build(canvas: HTMLCanvasElement) {
|
||||
static build(canvas?: HTMLCanvasElement) {
|
||||
return new GraphicsDeviceBuilder(canvas)
|
||||
}
|
||||
|
||||
|
@ -195,7 +195,7 @@ export class GraphicsDevice extends EventEmitter {
|
|||
return true
|
||||
}
|
||||
|
||||
createBuffer(descriptor: GPUBufferDescriptor, data: ArrayBufferView | ArrayBuffer): Buffer {
|
||||
createBuffer(descriptor: GPUBufferDescriptor, data?: ArrayBufferView | ArrayBuffer): Buffer {
|
||||
if (!this._isInitialized) { throw GraphicsDeviceError.uninitialized() }
|
||||
|
||||
try {
|
||||
|
@ -247,7 +247,7 @@ export class GraphicsDevice extends EventEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
createMaterial(shaders: ShaderPairStateDescriptor) {
|
||||
createMaterial(shaders: ShaderPairDescriptor) {
|
||||
if (!this._isInitialized) { throw GraphicsDeviceError.uninitialized() }
|
||||
|
||||
try {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
export class RenderPipeline {
|
||||
_handle
|
||||
_label
|
||||
_handle: GPURenderPipeline
|
||||
_label: string
|
||||
|
||||
get handle() {
|
||||
return this._handle
|
||||
|
@ -10,11 +10,7 @@ export class RenderPipeline {
|
|||
return this._label
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {GPURenderPipeline} pipeline
|
||||
* @param {string} [label]
|
||||
*/
|
||||
constructor(pipeline, label) {
|
||||
constructor(pipeline: GPURenderPipeline, label: string) {
|
||||
this._handle = pipeline
|
||||
this._label = label
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { WebGPUObjectError } from '../utils/errors.js'
|
||||
|
||||
export class BindGroupLayout {
|
||||
_device
|
||||
_handle
|
||||
_device: GPUDevice
|
||||
_handle: GPUBindGroupLayout
|
||||
|
||||
get handle() {
|
||||
return this._handle
|
||||
|
@ -12,20 +12,12 @@ export class BindGroupLayout {
|
|||
return this._handle.label
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {GPUDevice} device
|
||||
* @param {GPUBindGroupLayout} layout
|
||||
*/
|
||||
constructor(device, layout) {
|
||||
constructor(device: GPUDevice, layout: GPUBindGroupLayout) {
|
||||
this._device = device
|
||||
this._handle = layout
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {GPUDevice} device
|
||||
* @param {GPUBindGroupLayoutDescriptor} descriptor
|
||||
*/
|
||||
static create(device, descriptor) {
|
||||
static create(device: GPUDevice, descriptor: GPUBindGroupLayoutDescriptor) {
|
||||
try {
|
||||
return new BindGroupLayout(
|
||||
device,
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { WebGPUObjectError } from '../utils/errors.js'
|
||||
|
||||
export class BindGroup {
|
||||
_device
|
||||
_handle
|
||||
_device: GPUDevice
|
||||
_handle: GPUBindGroup
|
||||
|
||||
get handle() {
|
||||
return this._handle
|
||||
|
@ -12,16 +12,12 @@ export class BindGroup {
|
|||
* @param {GPUDevice} device
|
||||
* @param {GPUBindGroup} bindGroup
|
||||
*/
|
||||
constructor(device, bindGroup) {
|
||||
constructor(device: GPUDevice, bindGroup: GPUBindGroup) {
|
||||
this._device = device
|
||||
this._handle = bindGroup
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {GPUDevice} device
|
||||
* @param {GPUBindGroupDescriptor} descriptor
|
||||
*/
|
||||
static create(device, descriptor) {
|
||||
static create(device: GPUDevice, descriptor: GPUBindGroupDescriptor) {
|
||||
try {
|
||||
return new BindGroup(
|
||||
device,
|
||||
|
|
|
@ -1,29 +1,24 @@
|
|||
import { Buffer } from './buffer.js'
|
||||
|
||||
/**
|
||||
* @typedef GeometryDescriptor
|
||||
* @param {Buffer} vertices
|
||||
* @param {number} [vertexCount]
|
||||
* @param {GPUVertexBufferLayout} layout
|
||||
* @property {Buffer} [indices]
|
||||
* @property {number} [indexCount]
|
||||
* @property {GPUIndexFormat} [format]
|
||||
*/
|
||||
interface GeometryDescriptor {
|
||||
vertices: Buffer
|
||||
vertexCount: number
|
||||
layout: GPUVertexBufferLayout
|
||||
indices?: Buffer
|
||||
indexCount?: number
|
||||
format?: GPUIndexFormat
|
||||
}
|
||||
|
||||
export class Geometry {
|
||||
_device
|
||||
_vertices
|
||||
_vertexCount
|
||||
_vertexBufferLayout
|
||||
_indices
|
||||
_indexCount
|
||||
_format
|
||||
_device: GPUDevice
|
||||
_vertices: Buffer
|
||||
_vertexCount: number
|
||||
_vertexBufferLayout: GPUVertexBufferLayout
|
||||
_indices: Buffer
|
||||
_indexCount: number
|
||||
_format: string
|
||||
|
||||
/**
|
||||
* @param {GPUDevice} device
|
||||
* @param {GeometryDescriptor} descriptor
|
||||
*/
|
||||
constructor(device, descriptor) {
|
||||
constructor(device: GPUDevice, descriptor: GeometryDescriptor) {
|
||||
this._device = device
|
||||
this._vertices = descriptor.vertices
|
||||
this._vertexCount = descriptor.vertexCount
|
||||
|
|
|
@ -1,29 +1,33 @@
|
|||
import { BindGroupLayout } from './bind-group-layout.js'
|
||||
import { ShaderPair, ShaderModule } from './shader-module.js'
|
||||
import { FragmentStateDescriptor, ShaderPair, ShaderPairDescriptor, VertexStateDescriptor } from './shader-module.js'
|
||||
import { MaterialError, WebGPUObjectError } from '../utils/errors.js'
|
||||
import { ResourceType } from 'wgsl_reflect'
|
||||
import { BindGroup } from './bind-group.js'
|
||||
import { Texture } from './texture.js'
|
||||
import { Buffer } from './buffer.js'
|
||||
import { Sampler } from './sampler.js'
|
||||
|
||||
/** @import { FragmentStateDescriptor, VertexStateDescriptor } from './shader-module.js' */
|
||||
|
||||
/**
|
||||
* @typedef ShaderPairDescriptor
|
||||
* @property {ShaderModule} vertex
|
||||
* @property {ShaderModule} [fragment]
|
||||
*/
|
||||
type BindingResource = Buffer | Texture | Sampler
|
||||
|
||||
/**
|
||||
* @typedef {
|
||||
ShaderPairDescriptor &
|
||||
{ bindGroupLayouts?: BindGroupLayout[] }
|
||||
* } MaterialDescriptor
|
||||
*/
|
||||
interface MaterialPipelineDescriptor {
|
||||
label?: string
|
||||
pipelineLayout?: GPUPipelineLayout
|
||||
vertex: VertexStateDescriptor
|
||||
fragment?: FragmentStateDescriptor
|
||||
primitive?: GPUPrimitiveState
|
||||
}
|
||||
|
||||
interface MaterialDescriptor extends ShaderPairDescriptor {
|
||||
bindGroupLayouts?: BindGroupLayout[]
|
||||
}
|
||||
|
||||
export class Material {
|
||||
_device
|
||||
_shaders
|
||||
_bindGroupLayouts
|
||||
_pipelineLayout
|
||||
_device: GPUDevice
|
||||
_shaders: ShaderPair
|
||||
_bindGroupLayouts: BindGroupLayout[]
|
||||
_pipelineLayout: GPUPipelineLayout
|
||||
|
||||
get shaders() {
|
||||
return this._shaders
|
||||
|
@ -33,11 +37,7 @@ export class Material {
|
|||
return this._bindGroupLayouts
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {GPUDevice} device
|
||||
* @param {MaterialDescriptor} descriptor
|
||||
*/
|
||||
constructor(device, descriptor) {
|
||||
constructor(device: GPUDevice, descriptor: MaterialDescriptor) {
|
||||
this._device = device
|
||||
this._shaders = Material._reflectShaders(descriptor)
|
||||
const bgl = descriptor.bindGroupLayouts
|
||||
|
@ -58,14 +58,8 @@ export class Material {
|
|||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Attempts to handle shader modules which represent multiple
|
||||
* shader types.
|
||||
*
|
||||
* @param {ShaderPairDescriptor} shaders
|
||||
* @returns {ShaderPair}
|
||||
*/
|
||||
static _reflectShaders(shaders) {
|
||||
|
||||
static _reflectShaders(shaders: ShaderPairDescriptor): ShaderPair {
|
||||
if (shaders == null) {
|
||||
throw MaterialError.missingShader('vertex')
|
||||
}
|
||||
|
@ -75,20 +69,12 @@ export class Material {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {GPUDevice} device
|
||||
* @param {ShaderPair} shaders
|
||||
* @returns {BindGroupLayout[]}
|
||||
*/
|
||||
_reflectBindGroupLayouts(device, shaders) {
|
||||
_reflectBindGroupLayouts(device: GPUDevice, shaders: ShaderPair): BindGroupLayout[] {
|
||||
const layouts = shaders.createBindGroupLayoutEntries()
|
||||
return layouts.map(entries => BindGroupLayout.create(device, { entries }))
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
createBindGroup(groupIndex, resources, label) {
|
||||
createBindGroup(groupIndex: number, resources: Record<PropertyKey, BindingResource>, label?: string) {
|
||||
if (groupIndex < 0 || groupIndex >= this._bindGroupLayouts.length) {
|
||||
throw new Error(`Invalid bind group index: ${groupIndex}`)
|
||||
}
|
||||
|
@ -122,20 +108,7 @@ export class Material {
|
|||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef MaterialPipelineDescriptor
|
||||
* @property {string} [label]
|
||||
* @property {GPUPipelineLayout} [pipelineLayout]
|
||||
* @property {VertexStateDescriptor} vertex
|
||||
* @property {FragmentStateDescriptor} [fragment]
|
||||
* @property {GPUPrimitiveState} [primitive]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {MaterialPipelineDescriptor} descriptor
|
||||
* @returns {GPURenderPipelineDescriptor}
|
||||
*/
|
||||
getRenderPipelineDescriptor(descriptor) {
|
||||
getRenderPipelineDescriptor(descriptor: MaterialPipelineDescriptor): GPURenderPipelineDescriptor {
|
||||
const { fragment, vertex } = this.shaders.getRenderPipelineStates(descriptor)
|
||||
|
||||
return {
|
||||
|
|
|
@ -4,8 +4,8 @@ import { ResourceType } from '../utils/internal-enums.js'
|
|||
/** @import { BindGroupEntry } from '../core/graphics-device.js' */
|
||||
|
||||
export class Sampler {
|
||||
_device
|
||||
_handle
|
||||
_device: GPUDevice
|
||||
_handle: GPUSampler
|
||||
|
||||
get handle() {
|
||||
return this._handle
|
||||
|
@ -19,20 +19,12 @@ export class Sampler {
|
|||
return ResourceType.Sampler
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {GPUDevice} device
|
||||
* @param {GPUSampler} sampler
|
||||
*/
|
||||
constructor(device, sampler) {
|
||||
constructor(device: GPUDevice, sampler: GPUSampler) {
|
||||
this._device = device
|
||||
this._handle = sampler
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {GPUDevice} device
|
||||
* @param {GPUSamplerDescriptor} descriptor
|
||||
*/
|
||||
static create(device, descriptor) {
|
||||
static create(device: GPUDevice, descriptor: GPUSamplerDescriptor) {
|
||||
try {
|
||||
return new Sampler(
|
||||
device,
|
||||
|
|
|
@ -69,6 +69,11 @@ export interface ShaderPairStateDescriptor {
|
|||
vertex: VertexStateDescriptor
|
||||
}
|
||||
|
||||
export interface ShaderPairDescriptor {
|
||||
fragment?: ShaderModule
|
||||
vertex: ShaderModule
|
||||
}
|
||||
|
||||
export class ReflectedShader {
|
||||
static _reflectTypes = ['uniforms', 'storage', 'textures', 'samplers']
|
||||
_module: ShaderModule
|
||||
|
@ -292,10 +297,7 @@ export class ShaderPair {
|
|||
)
|
||||
}
|
||||
|
||||
static fromPair(value: {
|
||||
vertex: ShaderModule
|
||||
fragment?: ShaderModule
|
||||
}) {
|
||||
static fromPair(value: ShaderPairDescriptor) {
|
||||
const vert = new ReflectedShader(value.vertex)
|
||||
const frag = value.fragment && new ReflectedShader(value.fragment)
|
||||
return new ShaderPair(vert, frag)
|
||||
|
|
|
@ -1,21 +1,24 @@
|
|||
import { CommandRecorder } from '../core/command-recorder.js'
|
||||
import { BitFlags } from '../utils/bitflags.js'
|
||||
import { TextureDimension, TextureFormat } from '../enum.js'
|
||||
import { WebGPUObjectError } from '../utils/errors.js'
|
||||
import { ResourceType } from '../utils/internal-enums.js'
|
||||
import { textureToImageDimension } from '../utils/wgsl-to-wgpu.js'
|
||||
|
||||
/** @import { BindGroupEntry } from '../core/graphics-device.js' */
|
||||
interface UploadTextureInfo {
|
||||
destination: GPUTexelCopyTextureInfo
|
||||
dataLayout: GPUTexelCopyBufferLayout
|
||||
size: GPUExtent3DStrict
|
||||
}
|
||||
|
||||
export class Texture {
|
||||
static _defaultUsage = GPUTextureUsage.TEXTURE_BINDING
|
||||
| GPUTextureUsage.COPY_DST
|
||||
| GPUTextureUsage.RENDER_ATTACHMENT
|
||||
|
||||
_device
|
||||
_handle
|
||||
_device: GPUDevice
|
||||
_handle: GPUTexture
|
||||
|
||||
/** @type {GPUTextureView | undefined} */
|
||||
_defaultView
|
||||
_defaultView: GPUTextureView | undefined
|
||||
|
||||
get handle() {
|
||||
return this._handle
|
||||
|
@ -57,20 +60,12 @@ export class Texture {
|
|||
return ResourceType.TextureView
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {GPUDevice} device
|
||||
* @param {GPUTexture} texture
|
||||
*/
|
||||
constructor(device, texture) {
|
||||
constructor(device: GPUDevice, texture: GPUTexture) {
|
||||
this._device = device
|
||||
this._handle = texture
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {GPUDevice} device
|
||||
* @param {GPUTextureDescriptor} descriptor
|
||||
*/
|
||||
static create(device, descriptor) {
|
||||
static create(device: GPUDevice, descriptor: GPUTextureDescriptor) {
|
||||
try {
|
||||
return new Texture(
|
||||
device,
|
||||
|
@ -81,18 +76,13 @@ export class Texture {
|
|||
}
|
||||
}
|
||||
|
||||
static _generateMipLevels(size) {
|
||||
static _generateMipLevels(size: number[]) {
|
||||
const max = Math.max.apply(undefined, size)
|
||||
return 1 + Math.log2(max) | 0
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {GPUDevice} device
|
||||
* @param {string | URL} url
|
||||
* @param {GPUTextureDescriptor} desciptor
|
||||
*/
|
||||
static async fromUrl(device, url, descriptor) {
|
||||
static async fromUrl(device: GPUDevice, url: string | URL, descriptor: GPUTextureDescriptor) {
|
||||
try {
|
||||
const response = await fetch(url)
|
||||
|
||||
|
@ -100,8 +90,8 @@ export class Texture {
|
|||
throw new Error(`Failed to fetch remote resource: ${response.statusText}`)
|
||||
}
|
||||
|
||||
const usage = options.usage || Texture._defaultUsage
|
||||
const dimension = descriptor.dimension ? textureToImageDimension(descriptor.dimension) : '2d'
|
||||
const usage = descriptor.usage || Texture._defaultUsage
|
||||
const dimension = descriptor.dimension ? textureToImageDimension(descriptor.dimension) : TextureDimension['2d']
|
||||
|
||||
const blob = await response.blob()
|
||||
const bitmap = await createImageBitmap(blob)
|
||||
|
@ -111,8 +101,8 @@ export class Texture {
|
|||
usage,
|
||||
dimension,
|
||||
size,
|
||||
format: descriptor.format || 'rgba8unorm',
|
||||
mipLevelCount: descriptor.mipLevelCount || Texture._generateMipCount(...size),
|
||||
format: descriptor.format || TextureFormat.Rgba8unorm,
|
||||
mipLevelCount: descriptor.mipLevelCount || Texture._generateMipLevels(size),
|
||||
...descriptor,
|
||||
}
|
||||
|
||||
|
@ -126,13 +116,7 @@ export class Texture {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {GPUDevice} device
|
||||
* @param {GPUExtent3DStrict} size
|
||||
* @param {GPUTextureFormat} format
|
||||
* @param {GPUTextureDescriptor} descriptor
|
||||
*/
|
||||
static createRenderTarget(device, size, format, descriptor) {
|
||||
static createRenderTarget(device: GPUDevice, size: GPUExtent3DStrict, format: GPUTextureFormat, descriptor: GPUTextureDescriptor) {
|
||||
const usage = descriptor.usage || Texture._defaultUsage
|
||||
|
||||
return Texture.create(device, {
|
||||
|
@ -143,18 +127,8 @@ export class Texture {
|
|||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef UploadTextureInfo
|
||||
* @property {GPUTexelCopyTextureInfo} destination
|
||||
* @property {GPUTexelCopyBufferLayout} dataLayout
|
||||
* @property {GPUExtent3DStrict} size
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {GPUAllowSharedBufferSource} source
|
||||
* @param {UploadTextureInfo} [options={}]
|
||||
*/
|
||||
upload(source, options = {}) {
|
||||
upload(source: GPUAllowSharedBufferSource, options?: UploadTextureInfo) {
|
||||
const mipLevel = options.destination.mipLevel || 0
|
||||
const size = options.size || [
|
||||
Math.max(1, this.width >> mipLevel),
|
||||
|
@ -174,28 +148,11 @@ export class Texture {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {GPUCommandEncoder} commandEncoder
|
||||
*/
|
||||
generateMipmaps(commandEncoder) {
|
||||
const requiredUsage = GPUTextureUsage.COPY_SRC
|
||||
| GPUTextureUsage.COPY_DST
|
||||
| GPUTextureUsage.RENDER_ATTACHMENT
|
||||
|
||||
if (!BitFlags.has(this.usage & requiredUsage)) {
|
||||
throw new Error('Texture does not have the required usage flags for mipmap generation')
|
||||
}
|
||||
|
||||
for (let i = 1; i < this.mipLevelCount; ++i) {
|
||||
|
||||
}
|
||||
generateMipmaps(_commandEncoder: GPUCommandEncoder) {
|
||||
// TODO: use MipGenerator
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {GPUTextureViewDescriptor} [descriptor]
|
||||
* @throws {TextureError}
|
||||
*/
|
||||
createDefaultView(descriptor) {
|
||||
createDefaultView(descriptor?: GPUTextureViewDescriptor) {
|
||||
if (!descriptor && this._defaultView) {
|
||||
return this._defaultView
|
||||
}
|
||||
|
|
|
@ -1,73 +1,49 @@
|
|||
/**
|
||||
* @typedef {'read' | 'write' | 'read_write'} WGSLAccess
|
||||
* @typedef {'f32' | 'i32' | 'u32'} WGSLSampledType
|
||||
*/
|
||||
|
||||
import { BufferBindingType, StorageTextureAccess, TextureFormat } from '../enum.js'
|
||||
|
||||
/**
|
||||
* @typedef {
|
||||
'texture_1d'
|
||||
| 'texture_2d'
|
||||
| 'texture_2d_array'
|
||||
| 'texture_3d'
|
||||
| 'texture_cube'
|
||||
| 'texture_cube_array'
|
||||
* } WGSLSampledTextureType
|
||||
*
|
||||
* @typedef {
|
||||
'texture_multisampled_2d'
|
||||
| 'texture_depth_multisampled_2d'
|
||||
* } WGSLMultisampledTextureType
|
||||
*
|
||||
* @typedef {
|
||||
'texture_storage_1d'
|
||||
| 'texture_storage_2d'
|
||||
| 'texture_storage_2d_array'
|
||||
| 'texture_storage_3d'
|
||||
* } WGSLStorageTextureType
|
||||
*
|
||||
* @typedef {
|
||||
'texture_depth_2d'
|
||||
| 'texture_depth_2d_array'
|
||||
| 'texture_depth_cube'
|
||||
| 'texture_depth_cube_array'
|
||||
* } WGSLDepthTextureType
|
||||
*
|
||||
* @typedef {
|
||||
WGSLSampledTextureType
|
||||
| WGSLMultisampledTextureType
|
||||
| WGSLStorageTextureType
|
||||
| WGSLDepthTextureType
|
||||
* } WGSLTextureType
|
||||
*/
|
||||
export type WGSLAccess = 'read' | 'write' | 'read_write'
|
||||
export type WGSLSampledType = 'f32' | 'i32' | 'u32'
|
||||
export type WGSLSampledTextureType = 'texture_1d'
|
||||
| 'texture_2d'
|
||||
| 'texture_2d_array'
|
||||
| 'texture_3d'
|
||||
| 'texture_cube'
|
||||
| 'texture_cube_array'
|
||||
|
||||
/**
|
||||
* @typedef {
|
||||
'sampler'
|
||||
| 'sampler_comparison'
|
||||
* } WGSLSamplerType
|
||||
*/
|
||||
export type WGSLMultisampledTextureType = 'texture_multisampled_2d'
|
||||
| 'texture_depth_multisampled_2d'
|
||||
|
||||
/**
|
||||
* @param {string} typeName
|
||||
* @returns {[WGSLTextureType, WGSLSampledType]}
|
||||
*/
|
||||
export const parseTextureType = (typeName) => {
|
||||
export type WGSLStorageTextureType = 'texture_storage_1d'
|
||||
| 'texture_storage_2d'
|
||||
| 'texture_storage_2d_array'
|
||||
| 'texture_storage_3d'
|
||||
|
||||
export type WGSLDepthTextureType = 'texture_depth_2d'
|
||||
| 'texture_depth_2d_array'
|
||||
| 'texture_depth_cube'
|
||||
| 'texture_depth_cube_array'
|
||||
|
||||
export type WGSLTextureType = WGSLSampledTextureType
|
||||
| WGSLMultisampledTextureType
|
||||
| WGSLStorageTextureType
|
||||
| WGSLDepthTextureType
|
||||
|
||||
export type WGSLSamplerType = 'sampler'
|
||||
| 'sampler_comparison'
|
||||
|
||||
export const parseTextureType = (typeName: string): [WGSLTextureType, WGSLSampledType] => {
|
||||
const chevronIndex = typeName.indexOf('<')
|
||||
const type = typeName.slice(0, chevronIndex)
|
||||
const sampledType = typeName.slice(chevronIndex + 1, -1)
|
||||
|
||||
// FIXME: there's no validation
|
||||
return [
|
||||
/** @type {WGSLTextureType} */ (type),
|
||||
/** @type {WGSLSampledType} */ (sampledType)
|
||||
type as WGSLTextureType,
|
||||
sampledType as WGSLSampledType
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {WGSLAccess} access
|
||||
* @returns {GPUBufferBindingType}
|
||||
*/
|
||||
export const accessToBufferType = access => {
|
||||
export const accessToBufferType = (access: WGSLAccess): GPUBufferBindingType => {
|
||||
switch (access) {
|
||||
case 'read': return BufferBindingType.ReadOnlyStorage
|
||||
case 'write':
|
||||
|
@ -77,11 +53,7 @@ export const accessToBufferType = access => {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {WGSLAccess} access
|
||||
* @returns {GPUStorageTextureAccess}
|
||||
*/
|
||||
export const accessToStorageTextureAccess = access => {
|
||||
export const accessToStorageTextureAccess = (access: WGSLAccess): GPUStorageTextureAccess => {
|
||||
switch (access) {
|
||||
case 'read': return StorageTextureAccess.ReadOnly
|
||||
case 'write': return StorageTextureAccess.WriteOnly
|
||||
|
@ -135,8 +107,7 @@ export const FormatToFilterType = Object.freeze({
|
|||
stencil8: 'uint'
|
||||
})
|
||||
|
||||
/** @param {string} format */
|
||||
export const formatToFilterType = format => (
|
||||
export const formatToFilterType = (format: string) => (
|
||||
FormatToFilterType[format] || 'float'
|
||||
)
|
||||
|
||||
|
@ -186,14 +157,12 @@ export const FormatToStride = Object.freeze({
|
|||
stencil8: 1
|
||||
})
|
||||
|
||||
/** @param {string} typeName */
|
||||
export const typeToStride = typeName => (
|
||||
export const typeToStride = (typeName: string) => (
|
||||
FormatToStride[typeName] || 1
|
||||
)
|
||||
|
||||
|
||||
/** @param {WGSLTextureType} typeName */
|
||||
export const typeToViewDimension = typeName => {
|
||||
export const typeToViewDimension = (typeName: WGSLTextureType) => {
|
||||
switch (typeName) {
|
||||
case 'texture_1d':
|
||||
case 'texture_storage_1d':
|
||||
|
@ -228,11 +197,7 @@ export const typeToViewDimension = typeName => {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {GPUTextureViewDimension} dimension
|
||||
* @returns {GPUTextureDimension}
|
||||
*/
|
||||
export const textureToImageDimension = dimension => {
|
||||
export const textureToImageDimension = (dimension: GPUTextureViewDimension): GPUTextureDimension => {
|
||||
switch (dimension) {
|
||||
case '1d':
|
||||
return '1d'
|
||||
|
@ -250,11 +215,7 @@ export const textureToImageDimension = dimension => {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} format
|
||||
* @returns {GPUTextureFormat}
|
||||
*/
|
||||
export const wgslToWgpuFormat = (format) => {
|
||||
export const wgslToWgpuFormat = (format: string): GPUTextureFormat => {
|
||||
switch (format) {
|
||||
case 'f32':
|
||||
return TextureFormat.Bgra8unorm
|
||||
|
@ -299,12 +260,7 @@ export const wgslToWgpuFormat = (format) => {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {WGSLTextureType} type
|
||||
* @param {WGSLSampledType} sampledType
|
||||
* @returns {GPUTextureSampleType}
|
||||
*/
|
||||
export const typeToTextureSampleType = (type, sampledType) => {
|
||||
export const typeToTextureSampleType = (type: WGSLTextureType, sampledType: WGSLSampledType): GPUTextureSampleType => {
|
||||
if (type.includes('depth')) {
|
||||
return 'depth'
|
||||
}
|
||||
|
@ -318,11 +274,7 @@ export const typeToTextureSampleType = (type, sampledType) => {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {WGSLSamplerType} type
|
||||
* @returns {GPUSamplerBindingType}
|
||||
*/
|
||||
export const typeToSamplerBindingType = type => {
|
||||
export const typeToSamplerBindingType = (type: WGSLSamplerType): GPUSamplerBindingType => {
|
||||
switch (type) {
|
||||
case 'sampler_comparison':
|
||||
return 'comparison'
|
||||
|
|
Loading…
Add table
Reference in a new issue