Compare commits

..

No commits in common. "9a54bb90c93cd38a6f5d9809a9df417339091825" and "80704056b1b6c5c31c55c290422e387d37a9c737" have entirely different histories.

14 changed files with 123 additions and 236 deletions

View file

@ -2,7 +2,7 @@ import { LoadOp, StoreOp } from '../enum.js'
import { CommandRecorderError } from '../utils/errors.js' import { CommandRecorderError } from '../utils/errors.js'
import { SwapChain } from './swap-chain.js' import { SwapChain } from './swap-chain.js'
export class CommandEncoder implements GPUCommandEncoder { export class CommandRecorder {
static _defaultClearValue = { r: 0, g: 0, b: 0, a: 1 } static _defaultClearValue = { r: 0, g: 0, b: 0, a: 1 }
_device: GPUDevice _device: GPUDevice
@ -29,24 +29,24 @@ export class CommandEncoder implements GPUCommandEncoder {
return [{ return [{
view, view,
clearValue: CommandEncoder._defaultClearValue, clearValue: CommandRecorder._defaultClearValue,
loadOp: LoadOp.Clear, loadOp: LoadOp.Clear,
storeOp: StoreOp.Store storeOp: StoreOp.Store
}] }]
} }
beginRenderPass(descriptor: GPURenderPassDescriptor): GPURenderPassEncoder { beginRenderPass(colorAttachments?: GPURenderPassColorAttachment[], depthStencilAttachment?: GPURenderPassDepthStencilAttachment): GPURenderPassEncoder {
if (this._passEncoder) { if (this._passEncoder) {
throw CommandRecorderError.activeRenderPass() throw CommandRecorderError.activeRenderPass()
} }
const attachments = colorAttachments || this._defaultColorAttachment() const attachments = colorAttachments || this._defaultColorAttachment()
//const descriptor = { const descriptor = {
// label: this.label || 'RenderPass', label: this.label || 'RenderPass',
// colorAttachments: attachments, colorAttachments: attachments,
// depthStencilAttachment depthStencilAttachment
//} }
this._passEncoder = this._encoder.beginRenderPass(descriptor) this._passEncoder = this._encoder.beginRenderPass(descriptor)

View file

@ -1,10 +1,11 @@
import { SwapChain } from './swap-chain.js' import { SwapChain } from './swap-chain.js'
import { Buffer, UniformBuffer } from '../resources/buffer.js' import { Buffer, UniformBuffer } from '../resources/buffer.js'
import { GraphicsDeviceError, WebGPUError, BufferError, WebGPUObjectError } from '../utils/errors.js' import { GraphicsDeviceError, WebGPUError, BufferError, WebGPUObjectError } from '../utils/errors.js'
import { EventEmitter, GraphicsDeviceInitialized, GraphicsDeviceLost } from '../utils/events.js' import { GraphicsDeviceInitialized, GraphicsDeviceLost } from '../utils/events.js'
import { EventEmitter } from '../utils/index.js'
import { ShaderModule, ShaderPairDescriptor } from '../resources/shader-module.js' import { ShaderModule, ShaderPairDescriptor } from '../resources/shader-module.js'
import { RenderPipeline } from '../rendering/render-pipeline.js' import { RenderPipeline } from '../rendering/render-pipeline.js'
import { CommandEncoder } from './command-recorder.js' import { CommandRecorder } from './command-recorder.js'
import { BindGroupLayout } from '../resources/bind-group-layout.js' import { BindGroupLayout } from '../resources/bind-group-layout.js'
import { BindGroup } from '../resources/bind-group.js' import { BindGroup } from '../resources/bind-group.js'
import { Texture } from '../resources/texture.js' import { Texture } from '../resources/texture.js'
@ -124,9 +125,7 @@ class DeviceHandler {
} }
} }
export class GraphicsDevice extends EventEmitter implements GPUDevice { export class GraphicsDevice extends EventEmitter {
__brand: 'GPUDevice'
_canvas: HTMLCanvasElement _canvas: HTMLCanvasElement
_deviceHandler: DeviceHandler _deviceHandler: DeviceHandler
_swapChain: SwapChain _swapChain: SwapChain
@ -154,34 +153,6 @@ export class GraphicsDevice extends EventEmitter implements GPUDevice {
return this._swapChain return this._swapChain
} }
get features() {
return this.device.features
}
get limits() {
return this.device.limits
}
get adapterInfo() {
return this.device.adapterInfo
}
get lost() {
return this.device.lost
}
get onuncapturederror() {
return this.device.onuncapturederror
}
set onuncapturederror(value) {
this.device.onuncapturederror = value
}
get label() {
return this.device.label
}
constructor(canvas: HTMLCanvasElement, deviceHandler: DeviceHandler) { constructor(canvas: HTMLCanvasElement, deviceHandler: DeviceHandler) {
super() super()
@ -189,34 +160,6 @@ export class GraphicsDevice extends EventEmitter implements GPUDevice {
this._deviceHandler = deviceHandler this._deviceHandler = deviceHandler
} }
importExternalTexture(descriptor: GPUExternalTextureDescriptor): GPUExternalTexture {
throw new Error('Method not implemented.')
}
createComputePipeline(descriptor: GPUComputePipelineDescriptor): GPUComputePipeline {
throw new Error('Method not implemented.')
}
createComputePipelineAsync(descriptor: GPUComputePipelineDescriptor): Promise<GPUComputePipeline> {
throw new Error('Method not implemented.')
}
createRenderPipelineAsync(descriptor: GPURenderPipelineDescriptor): Promise<GPURenderPipeline> {
throw new Error('Method not implemented.')
}
createCommandEncoder(descriptor?: GPUCommandEncoderDescriptor): GPUCommandEncoder {
throw new Error('Method not implemented.')
}
createRenderBundleEncoder(descriptor: GPURenderBundleEncoderDescriptor): GPURenderBundleEncoder {
throw new Error('Method not implemented.')
}
createQuerySet(descriptor: GPUQuerySetDescriptor): GPUQuerySet {
throw new Error('Method not implemented.')
}
pushErrorScope(filter: GPUErrorFilter): undefined {
throw new Error('Method not implemented.')
}
popErrorScope(): Promise<GPUError | null> {
throw new Error('Method not implemented.')
}
static build(canvas?: HTMLCanvasElement) { static build(canvas?: HTMLCanvasElement) {
return new GraphicsDeviceBuilder(canvas) return new GraphicsDeviceBuilder(canvas)
} }
@ -283,11 +226,11 @@ export class GraphicsDevice extends EventEmitter implements GPUDevice {
return buffer return buffer
} }
createShaderModule(descriptor: GPUShaderModuleDescriptor): ShaderModule { createShaderModule(code: string, label: string): ShaderModule {
if (!this._isInitialized) { throw GraphicsDeviceError.uninitialized() } if (!this._isInitialized) { throw GraphicsDeviceError.uninitialized() }
try { try {
return ShaderModule.create(this.device, descriptor) return ShaderModule.create(this.device, { code, label })
} catch (err) { } catch (err) {
throw WebGPUObjectError.from(err, ShaderModule) throw WebGPUObjectError.from(err, ShaderModule)
} }
@ -314,16 +257,19 @@ export class GraphicsDevice extends EventEmitter implements GPUDevice {
} }
} }
createCommandRecorder(label?: string): CommandEncoder { createCommandRecorder(label?: string): CommandRecorder {
if (!this._isInitialized) { throw GraphicsDeviceError.uninitialized() } if (!this._isInitialized) { throw GraphicsDeviceError.uninitialized() }
return new CommandEncoder(this.device, this._swapChain, label) return new CommandRecorder(this.device, this._swapChain, label)
} }
createBindGroupLayout(descriptor: GPUBindGroupLayoutDescriptor) { createBindGroupLayout(entries: GPUBindGroupLayoutEntry[], label: string) {
if (!this._isInitialized) { throw GraphicsDeviceError.uninitialized() } if (!this._isInitialized) { throw GraphicsDeviceError.uninitialized() }
return BindGroupLayout.create(this.device, descriptor) return BindGroupLayout.create(this.device, {
label,
entries
})
} }
_getBindingResource(binding: BindGroupEntry): GPUBindingResource { _getBindingResource(binding: BindGroupEntry): GPUBindingResource {
@ -357,24 +303,31 @@ export class GraphicsDevice extends EventEmitter implements GPUDevice {
} }
} }
createBindGroup(descriptor: GPUBindGroupDescriptor) { createBindGroup(layout: BindGroupLayout, bindings: BindGroupEntry[], label?: string) {
if (!this._isInitialized) { throw GraphicsDeviceError.uninitialized() } if (!this._isInitialized) { throw GraphicsDeviceError.uninitialized() }
//const entries = bindings.map(def => ({ const entries = bindings.map(def => ({
// binding: def.binding, binding: def.binding,
// resource: this._getBindingResource(def) resource: this._getBindingResource(def)
//})) }))
return BindGroup.create(this.device, descriptor) return BindGroup.create(this.device, {
layout: layout.handle,
entries,
label
})
} }
createPipelineLayout(descriptor: GPUPipelineLayoutDescriptor) { createPipelineLayout(layouts: Array<BindGroupLayout>, label?: string) {
if (!this._isInitialized) { throw GraphicsDeviceError.uninitialized() } if (!this._isInitialized) { throw GraphicsDeviceError.uninitialized() }
//const bindGroupLayouts = layouts.map(layout => layout.handle) const bindGroupLayouts = layouts.map(layout => layout.handle)
return this.device.createPipelineLayout(descriptor) return this.device.createPipelineLayout({
label,
bindGroupLayouts
})
} }
createSampler(descriptor?: GPUSamplerDescriptor) { createSampler(descriptor?: GPUSamplerDescriptor) {
@ -436,7 +389,10 @@ export class GraphicsDevice extends EventEmitter implements GPUDevice {
this.queue.submit(commandBuffers) this.queue.submit(commandBuffers)
} }
destroy(): undefined { /**
* Cleans up GPU resources. Call when the application exits.
*/
destroy() {
if (this.device) { if (this.device) {
this.device.destroy() this.device.destroy()
} }

View file

@ -1,6 +1,4 @@
export class RenderPipeline implements GPURenderPipeline { export class RenderPipeline {
__brand: 'GPURenderPipeline'
_handle: GPURenderPipeline _handle: GPURenderPipeline
_label: string _label: string
@ -16,9 +14,5 @@ export class RenderPipeline implements GPURenderPipeline {
this._handle = pipeline this._handle = pipeline
this._label = label this._label = label
} }
getBindGroupLayout(index: number): GPUBindGroupLayout {
return this.getBindGroupLayout(index)
}
} }

View file

@ -1,8 +1,6 @@
import { WebGPUObjectError } from '../utils/errors.js' import { WebGPUObjectError } from '../utils/errors.js'
export class BindGroupLayout implements GPUBindGroupLayout { export class BindGroupLayout {
__brand: 'GPUBindGroupLayout'
_device: GPUDevice _device: GPUDevice
_handle: GPUBindGroupLayout _handle: GPUBindGroupLayout

View file

@ -1,8 +1,6 @@
import { WebGPUObjectError } from '../utils/errors.js' import { WebGPUObjectError } from '../utils/errors.js'
export class BindGroup implements GPUBindGroup { export class BindGroup {
__brand: 'GPUBindGroup'
_device: GPUDevice _device: GPUDevice
_handle: GPUBindGroup _handle: GPUBindGroup
@ -10,10 +8,6 @@ export class BindGroup implements GPUBindGroup {
return this._handle return this._handle
} }
get label() {
return this._handle.label
}
/** /**
* @param {GPUDevice} device * @param {GPUDevice} device
* @param {GPUBindGroup} bindGroup * @param {GPUBindGroup} bindGroup

View file

@ -1,13 +1,11 @@
import { TypedArray, TypedArrayConstructor } from '../utils/index.js'
import { BufferError, WebGPUError } from '../utils/errors.js' import { BufferError, WebGPUError } from '../utils/errors.js'
import { TypedArray, TypedArrayConstructor } from '../utils/index.js'
import { ResourceType } from '../utils/internal-enums.js' import { ResourceType } from '../utils/internal-enums.js'
type SmallTypedArray = Exclude<TypedArray, BigInt64Array | BigUint64Array> type SmallTypedArray = Exclude<TypedArray, BigInt64Array | BigUint64Array>
type SmallTypedArrayConstructor = Exclude<TypedArrayConstructor, BigInt64ArrayConstructor | BigUint64ArrayConstructor> type SmallTypedArrayConstructor = Exclude<TypedArrayConstructor, BigInt64ArrayConstructor | BigUint64ArrayConstructor>
export class Buffer implements GPUBuffer { export class Buffer {
__brand: 'GPUBuffer'
_device: GPUDevice _device: GPUDevice
_handle: GPUBuffer _handle: GPUBuffer
@ -27,14 +25,6 @@ export class Buffer implements GPUBuffer {
return this._handle.usage return this._handle.usage
} }
get mapState() {
return this._handle.mapState
}
get label() {
return this._handle.label
}
get resourceType() { get resourceType() {
return ResourceType.Buffer return ResourceType.Buffer
} }
@ -43,6 +33,7 @@ export class Buffer implements GPUBuffer {
this._device = device this._device = device
this._handle = texture this._handle = texture
} }
static create(device: GPUDevice, descriptor: GPUBufferDescriptor) { static create(device: GPUDevice, descriptor: GPUBufferDescriptor) {
try { try {
return new Buffer( return new Buffer(
@ -162,20 +153,7 @@ export class Buffer implements GPUBuffer {
} }
} }
mapAsync(mode: GPUMapModeFlags, offset?: GPUSize64, size?: GPUSize64): Promise<undefined> { destroy() {
return this._handle.mapAsync(mode, offset, size)
}
getMappedRange(offset?: GPUSize64, size?: GPUSize64): ArrayBuffer {
return this._handle.getMappedRange(offset, size)
}
unmap(): undefined {
this._handle.unmap()
}
destroy(): undefined {
this._handle?.destroy() this._handle?.destroy()
this._handle = null this._handle = null
} }

View file

@ -92,6 +92,10 @@ export class Material {
resource: resource.toBindingResource() resource: resource.toBindingResource()
} }
if (variableInfo.resourceType === ResourceType.Uniform || variableInfo.resourceType === ResourceType.Storage) {
// TODO: handle user provided offset/size
}
entries.push(entry) entries.push(entry)
} }

View file

@ -1,9 +1,9 @@
import { WebGPUObjectError } from '../utils/errors.js' import { WebGPUObjectError } from '../utils/errors.js'
import { ResourceType } from '../utils/internal-enums.js' import { ResourceType } from '../utils/internal-enums.js'
export class Sampler implements GPUSampler { /** @import { BindGroupEntry } from '../core/graphics-device.js' */
__brand: 'GPUSampler'
export class Sampler {
_device: GPUDevice _device: GPUDevice
_handle: GPUSampler _handle: GPUSampler

View file

@ -13,8 +13,9 @@ import {
} from '../utils/wgsl-to-wgpu.js' } from '../utils/wgsl-to-wgpu.js'
import { BufferBindingType } from '../enum.js' import { BufferBindingType } from '../enum.js'
export class ShaderModule implements GPUShaderModule { /** @import { WGSLAccess, WGSLSamplerType } from '../utils/wgsl-to-wgpu.js' */
__brand: 'GPUShaderModule'
export class ShaderModule {
_handle: GPUShaderModule _handle: GPUShaderModule
_code: string _code: string
@ -43,17 +44,14 @@ export class ShaderModule implements GPUShaderModule {
} }
reflect() { reflect() {
if (!this._reflection) { if (this._reflection == null) {
this._reflection = new WgslReflect(this._code) this._reflection = new WgslReflect(this._code)
// no longer needed allow the GC to collect it
this._code = undefined this._code = undefined
} }
return this._reflection return this._reflection
} }
getCompilationInfo(): Promise<GPUCompilationInfo> {
return this._handle.getCompilationInfo()
}
} }
export interface FragmentStateDescriptor { export interface FragmentStateDescriptor {
@ -85,7 +83,7 @@ export class ReflectedShader {
} }
constructor(shader: ShaderModule, reflection: WgslReflect) { constructor(shader: ShaderModule) {
this._module = shader this._module = shader
} }

View file

@ -3,19 +3,18 @@ import { WebGPUObjectError } from '../utils/errors.js'
import { ResourceType } from '../utils/internal-enums.js' import { ResourceType } from '../utils/internal-enums.js'
import { textureToImageDimension } from '../utils/wgsl-to-wgpu.js' import { textureToImageDimension } from '../utils/wgsl-to-wgpu.js'
/** @import { BindGroupEntry } from '../core/graphics-device.js' */
interface UploadTextureInfo { interface UploadTextureInfo {
destination: GPUTexelCopyTextureInfo destination: GPUTexelCopyTextureInfo
dataLayout: GPUTexelCopyBufferLayout dataLayout: GPUTexelCopyBufferLayout
size: GPUExtent3DStrict size: GPUExtent3DStrict
} }
export class Texture implements GPUTexture { export class Texture {
static _defaultUsage = GPUTextureUsage.TEXTURE_BINDING static _defaultUsage = GPUTextureUsage.TEXTURE_BINDING
| GPUTextureUsage.COPY_DST | GPUTextureUsage.COPY_DST
| GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.RENDER_ATTACHMENT
__brand: 'GPUTexture'
_device: GPUDevice _device: GPUDevice
_handle: GPUTexture _handle: GPUTexture
@ -49,10 +48,6 @@ export class Texture implements GPUTexture {
return this._handle.dimension return this._handle.dimension
} }
get sampleCount() {
return this._handle.sampleCount
}
get mipLevelCount() { get mipLevelCount() {
return this._handle.mipLevelCount return this._handle.mipLevelCount
} }
@ -86,6 +81,7 @@ export class Texture implements GPUTexture {
return 1 + Math.log2(max) | 0 return 1 + Math.log2(max) | 0
} }
static async fromUrl(device: GPUDevice, url: string | URL, descriptor: GPUTextureDescriptor) { static async fromUrl(device: GPUDevice, url: string | URL, descriptor: GPUTextureDescriptor) {
try { try {
const response = await fetch(url) const response = await fetch(url)
@ -131,6 +127,7 @@ export class Texture implements GPUTexture {
}) })
} }
upload(source: GPUAllowSharedBufferSource, options?: UploadTextureInfo) { upload(source: GPUAllowSharedBufferSource, options?: UploadTextureInfo) {
const mipLevel = options.destination.mipLevel || 0 const mipLevel = options.destination.mipLevel || 0
const size = options.size || [ const size = options.size || [
@ -151,8 +148,8 @@ export class Texture implements GPUTexture {
} }
} }
createView(descriptor?: GPUTextureViewDescriptor) { generateMipmaps(_commandEncoder: GPUCommandEncoder) {
return this._handle.createView(descriptor) // TODO: use MipGenerator
} }
createDefaultView(descriptor?: GPUTextureViewDescriptor) { createDefaultView(descriptor?: GPUTextureViewDescriptor) {
@ -181,7 +178,7 @@ export class Texture implements GPUTexture {
return this.getView() return this.getView()
} }
destroy(): undefined { destroy() {
this._handle?.destroy() this._handle?.destroy()
this._handle = undefined this._handle = undefined
this._defaultView = undefined this._defaultView = undefined

View file

@ -1,54 +1,5 @@
import { GraphicsDevice } from '../core/graphics-device.js' import { GraphicsDevice } from '../core/graphics-device.js'
interface Listener {
(...args: any): void
}
export class EventEmitter {
_listeners = {}
addEventListener(type: PropertyKey, listener: Listener, options?: object): void {
this.on(type, listener, options)
}
removeEventListener(type: PropertyKey, listener: Listener, options?: object): void {
this.off(type, listener, options)
}
dispatchEvent(event: Event): boolean {
const details = 'detail' in event ? event.detail : undefined
return this.emit(event.type, ...Object.values(details))
}
on(event: PropertyKey, callback: Listener, _options?: object) {
this._listeners[event] = this._listeners[event] || []
this._listeners[event].push(callback)
}
emit(event: PropertyKey, ...args: any[]) {
const listeners = this._listeners[event]
if (listeners) {
listeners.forEach(
(cb: Listener) => cb(...args)
)
}
return true
}
off(event: PropertyKey, callback: Listener, _options?: object) {
const listeners = this._listeners[event]
if (listeners) {
this._listeners[event] = listeners.filter(
(cb: Listener) => cb !== callback
)
}
}
}
export class GraphicsDeviceInitialized { export class GraphicsDeviceInitialized {
static EventName = 'graphics-device:initialized' static EventName = 'graphics-device:initialized'
graphicsDevice: GraphicsDevice graphicsDevice: GraphicsDevice

View file

@ -88,6 +88,38 @@ export const FlagEnum = <const T extends string, const A extends T[]>(...values:
) as FlagEnum<T, A> ) as FlagEnum<T, A>
interface Listener {
(...args: any): void
}
export class EventEmitter {
_listeners = {}
on(event: PropertyKey, callback: Listener) {
this._listeners[event] = this._listeners[event] || []
this._listeners[event].push(callback)
}
emit(event: PropertyKey, ...args: any[]) {
const listeners = this._listeners[event]
if (listeners) {
listeners.forEach(
(cb: Listener) => cb(...args)
)
}
}
off(event: PropertyKey, callback: Listener) {
const listeners = this._listeners[event]
if (listeners) {
this._listeners[event] = listeners.filter(
(cb: Listener) => cb !== callback
)
}
}
}
export const pick = <T extends object, K extends keyof T>(obj: T, ...keys: K[]): Pick<T, K> => (Object.fromEntries( export const pick = <T extends object, K extends keyof T>(obj: T, ...keys: K[]): Pick<T, K> => (Object.fromEntries(
keys keys
.filter(key => key in obj) .filter(key => key in obj)

View file

@ -1,6 +1,3 @@
// taken from PixiJS
// https://pixijs.download/v8.0.0-rc.2/docs/rendering_renderers_gpu_texture_utils_GpuMipmapGenerator.ts.html
import code from './mip-shader.wgsl' import code from './mip-shader.wgsl'
const mip = (n: number) => Math.max(1, n >>> 1) const mip = (n: number) => Math.max(1, n >>> 1)
@ -84,37 +81,33 @@ export class MipGenerator {
return pipeline return pipeline
} }
generateMipmap(texture: GPUTexture, commandEncoder?: GPUCommandEncoder): GPUTexture { generateMipmap(texture: GPUTexture, descriptor: GPUTextureDescriptor) {
const pipeline = this.getPipeline(texture.format) const pipeline = this.getPipeline(descriptor.format)
const { dimension, format, mipLevelCount, width, height, depthOrArrayLayers } = texture if (descriptor.dimension !== '2d') {
const encoder = commandEncoder || this._device.createCommandEncoder()
if (texture.dimension !== '2d') {
throw new Error('Generating mipmaps for anything except 2d is unsupported.') throw new Error('Generating mipmaps for anything except 2d is unsupported.')
} }
let tempTexture = texture let mipTexture = texture
const { width, height, depthOrArrayLayers } = descriptor.size as GPUExtent3DDict
const renderToSource = descriptor.usage & GPUTextureUsage.RENDER_ATTACHMENT
const renderToSource = texture.usage & GPUTextureUsage.RENDER_ATTACHMENT
if (!renderToSource) { if (!renderToSource) {
tempTexture = this._device.createTexture({ mipTexture = this._device.createTexture({
size: { size: {
width: mip(width), width: mip(width),
height: mip(height), height: mip(height),
depthOrArrayLayers depthOrArrayLayers
}, },
format, format: descriptor.format,
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_SRC | GPUTextureUsage.RENDER_ATTACHMENT, usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_SRC | GPUTextureUsage.RENDER_ATTACHMENT,
mipLevelCount: mipLevelCount - 1, mipLevelCount: descriptor.mipLevelCount - 1
dimension,
sampleCount: 1
}) })
} }
const encoder = this._device.createCommandEncoder({})
for (let layer = 0; layer < depthOrArrayLayers; ++layer) { for (let layer = 0; layer < depthOrArrayLayers; ++layer) {
let srcView = texture.createView({ let srcView = texture.createView({
baseMipLevel: 0, baseMipLevel: 0,
@ -124,9 +117,10 @@ export class MipGenerator {
arrayLayerCount: 1 arrayLayerCount: 1
}) })
for (let i = 1; i < mipLevelCount; ++i) { let dstMipLevel = renderToSource ? 1 : 0
const dstView = tempTexture.createView({ for (let i = 1; i < descriptor.mipLevelCount; ++i) {
baseMipLevel: i - (renderToSource ? 0 : 1), const dstView = mipTexture.createView({
baseMipLevel: dstMipLevel++,
mipLevelCount: 1, mipLevelCount: 1,
dimension: '2d', dimension: '2d',
baseArrayLayer: layer, baseArrayLayer: layer,
@ -162,42 +156,33 @@ export class MipGenerator {
} }
if (!renderToSource) { if (!renderToSource) {
const mipSize = { const mipLevelSize = {
width: mip(width), width: mip(width),
height: mip(height), height: mip(height),
depthOrArrayLayers depthOrArrayLayers
} }
for (let i = 1; i < texture.mipLevelCount; ++i) { for (let i = 1; i < descriptor.mipLevelCount; ++i) {
encoder.copyTextureToTexture({ encoder.copyTextureToTexture({
texture: tempTexture, texture: mipTexture,
mipLevel: i - 1 mipLevel: i - 1
}, { }, {
texture, texture,
mipLevel: i, mipLevel: i,
}, mipSize) }, mipLevelSize)
mipSize.width = mip(mipSize.width) mipLevelSize.width = mip(mipLevelSize.width)
mipSize.height = mip(mipSize.height) mipLevelSize.height = mip(mipLevelSize.height)
} }
} }
this._device.queue.submit([encoder.finish()]) this._device.queue.submit([encoder.finish()])
if (!renderToSource) { if (!renderToSource) {
tempTexture.destroy() mipTexture.destroy()
} }
return texture return texture
} }
destroy() {
this._device = undefined
this._sampler = undefined
this._shader = undefined
this._pipelines = undefined
this._bindGroupLayout = undefined
this._pipelineLayout = undefined
}
} }

View file

@ -7,7 +7,7 @@ struct VertexOutput {
}; };
@vertex @vertex
fn vs_main(@builtin(vertex_index) vertexIndex : u32) -> VertexOutput { fn vertexMain(@builtin(vertex_index) vertexIndex : u32) -> VertexOutput {
var output : VertexOutput; var output : VertexOutput;
output.texCoord = pos[vertexIndex] * vec2<f32>(0.5, -0.5) + vec2<f32>(0.5); output.texCoord = pos[vertexIndex] * vec2<f32>(0.5, -0.5) + vec2<f32>(0.5);
output.position = vec4<f32>(pos[vertexIndex], 0.0, 1.0); output.position = vec4<f32>(pos[vertexIndex], 0.0, 1.0);
@ -18,6 +18,6 @@ fn vs_main(@builtin(vertex_index) vertexIndex : u32) -> VertexOutput {
@group(0) @binding(1) var img : texture_2d<f32>; @group(0) @binding(1) var img : texture_2d<f32>;
@fragment @fragment
fn fs_main(@location(0) texCoord : vec2<f32>) -> @location(0) vec4<f32> { fn fragmentMain(@location(0) texCoord : vec2<f32>) -> @location(0) vec4<f32> {
return textureSample(img, imgSampler, texCoord); return textureSample(img, imgSampler, texCoord);
} }