'use strict'; var adapter = require('../../../../environment/adapter.js'); var Extensions = require('../../../../extensions/Extensions.js'); var UniformGroup = require('../../shared/shader/UniformGroup.js'); var CanvasPool = require('../../shared/texture/CanvasPool.js'); var BindGroup = require('../shader/BindGroup.js'); var gpuUploadBufferImageResource = require('./uploaders/gpuUploadBufferImageResource.js'); var gpuUploadCompressedTextureResource = require('./uploaders/gpuUploadCompressedTextureResource.js'); var gpuUploadImageSource = require('./uploaders/gpuUploadImageSource.js'); var gpuUploadVideoSource = require('./uploaders/gpuUploadVideoSource.js'); var GpuMipmapGenerator = require('./utils/GpuMipmapGenerator.js'); "use strict"; class GpuTextureSystem { constructor(renderer) { this.managedTextures = []; this._gpuSources = /* @__PURE__ */ Object.create(null); this._gpuSamplers = /* @__PURE__ */ Object.create(null); this._bindGroupHash = /* @__PURE__ */ Object.create(null); this._textureViewHash = /* @__PURE__ */ Object.create(null); this._uploads = { image: gpuUploadImageSource.gpuUploadImageResource, buffer: gpuUploadBufferImageResource.gpuUploadBufferImageResource, video: gpuUploadVideoSource.gpuUploadVideoResource, compressed: gpuUploadCompressedTextureResource.gpuUploadCompressedTextureResource }; this._renderer = renderer; } contextChange(gpu) { this._gpu = gpu; } initSource(source) { if (source.autoGenerateMipmaps) { const biggestDimension = Math.max(source.pixelWidth, source.pixelHeight); source.mipLevelCount = Math.floor(Math.log2(biggestDimension)) + 1; } let usage = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST; if (source.uploadMethodId !== "compressed") { usage |= GPUTextureUsage.RENDER_ATTACHMENT; usage |= GPUTextureUsage.COPY_SRC; } const blockData = gpuUploadCompressedTextureResource.blockDataMap[source.format] || { blockBytes: 4, blockWidth: 1, blockHeight: 1 }; const width = Math.ceil(source.pixelWidth / blockData.blockWidth) * blockData.blockWidth; const height = Math.ceil(source.pixelHeight / blockData.blockHeight) * blockData.blockHeight; const textureDescriptor = { label: source.label, size: { width, height }, format: source.format, sampleCount: source.sampleCount, mipLevelCount: source.mipLevelCount, dimension: source.dimension, usage }; const gpuTexture = this._gpu.device.createTexture(textureDescriptor); this._gpuSources[source.uid] = gpuTexture; if (!this.managedTextures.includes(source)) { source.on("update", this.onSourceUpdate, this); source.on("resize", this.onSourceResize, this); source.on("destroy", this.onSourceDestroy, this); source.on("unload", this.onSourceUnload, this); source.on("updateMipmaps", this.onUpdateMipmaps, this); this.managedTextures.push(source); } this.onSourceUpdate(source); return gpuTexture; } onSourceUpdate(source) { const gpuTexture = this.getGpuSource(source); if (!gpuTexture) return; if (this._uploads[source.uploadMethodId]) { this._uploads[source.uploadMethodId].upload(source, gpuTexture, this._gpu); } if (source.autoGenerateMipmaps && source.mipLevelCount > 1) { this.onUpdateMipmaps(source); } } onSourceUnload(source) { const gpuTexture = this._gpuSources[source.uid]; if (gpuTexture) { this._gpuSources[source.uid] = null; gpuTexture.destroy(); } } onUpdateMipmaps(source) { if (!this._mipmapGenerator) { this._mipmapGenerator = new GpuMipmapGenerator.GpuMipmapGenerator(this._gpu.device); } const gpuTexture = this.getGpuSource(source); this._mipmapGenerator.generateMipmap(gpuTexture); } onSourceDestroy(source) { source.off("update", this.onSourceUpdate, this); source.off("unload", this.onSourceUnload, this); source.off("destroy", this.onSourceDestroy, this); source.off("resize", this.onSourceResize, this); source.off("updateMipmaps", this.onUpdateMipmaps, this); this.managedTextures.splice(this.managedTextures.indexOf(source), 1); this.onSourceUnload(source); } onSourceResize(source) { const gpuTexture = this._gpuSources[source.uid]; if (!gpuTexture) { this.initSource(source); } else if (gpuTexture.width !== source.pixelWidth || gpuTexture.height !== source.pixelHeight) { this._textureViewHash[source.uid] = null; this._bindGroupHash[source.uid] = null; this.onSourceUnload(source); this.initSource(source); } } _initSampler(sampler) { this._gpuSamplers[sampler._resourceId] = this._gpu.device.createSampler(sampler); return this._gpuSamplers[sampler._resourceId]; } getGpuSampler(sampler) { return this._gpuSamplers[sampler._resourceId] || this._initSampler(sampler); } getGpuSource(source) { return this._gpuSources[source.uid] || this.initSource(source); } /** * this returns s bind group for a specific texture, the bind group contains * - the texture source * - the texture style * - the texture matrix * This is cached so the bind group should only be created once per texture * @param texture - the texture you want the bindgroup for * @returns the bind group for the texture */ getTextureBindGroup(texture) { return this._bindGroupHash[texture.uid] ?? this._createTextureBindGroup(texture); } _createTextureBindGroup(texture) { const source = texture.source; this._bindGroupHash[texture.uid] = new BindGroup.BindGroup({ 0: source, 1: source.style, 2: new UniformGroup.UniformGroup({ uTextureMatrix: { type: "mat3x3", value: texture.textureMatrix.mapCoord } }) }); return this._bindGroupHash[texture.uid]; } getTextureView(texture) { const source = texture.source; return this._textureViewHash[source.uid] ?? this._createTextureView(source); } _createTextureView(texture) { this._textureViewHash[texture.uid] = this.getGpuSource(texture).createView(); return this._textureViewHash[texture.uid]; } generateCanvas(texture) { const renderer = this._renderer; const commandEncoder = renderer.gpu.device.createCommandEncoder(); const canvas = adapter.DOMAdapter.get().createCanvas(); canvas.width = texture.source.pixelWidth; canvas.height = texture.source.pixelHeight; const context = canvas.getContext("webgpu"); context.configure({ device: renderer.gpu.device, // eslint-disable-next-line max-len usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.COPY_SRC, format: adapter.DOMAdapter.get().getNavigator().gpu.getPreferredCanvasFormat(), alphaMode: "premultiplied" }); commandEncoder.copyTextureToTexture({ texture: renderer.texture.getGpuSource(texture.source), origin: { x: 0, y: 0 } }, { texture: context.getCurrentTexture() }, { width: canvas.width, height: canvas.height }); renderer.gpu.device.queue.submit([commandEncoder.finish()]); return canvas; } getPixels(texture) { const webGPUCanvas = this.generateCanvas(texture); const canvasAndContext = CanvasPool.CanvasPool.getOptimalCanvasAndContext(webGPUCanvas.width, webGPUCanvas.height); const context = canvasAndContext.context; context.drawImage(webGPUCanvas, 0, 0); const { width, height } = webGPUCanvas; const imageData = context.getImageData(0, 0, width, height); const pixels = new Uint8ClampedArray(imageData.data.buffer); CanvasPool.CanvasPool.returnCanvasAndContext(canvasAndContext); return { pixels, width, height }; } destroy() { this.managedTextures.slice().forEach((source) => this.onSourceDestroy(source)); this.managedTextures = null; for (const k of Object.keys(this._bindGroupHash)) { const key = Number(k); const bindGroup = this._bindGroupHash[key]; bindGroup?.destroy(); this._bindGroupHash[key] = null; } this._gpu = null; this._mipmapGenerator = null; this._gpuSources = null; this._bindGroupHash = null; this._textureViewHash = null; this._gpuSamplers = null; } } /** @ignore */ GpuTextureSystem.extension = { type: [ Extensions.ExtensionType.WebGPUSystem ], name: "texture" }; exports.GpuTextureSystem = GpuTextureSystem; //# sourceMappingURL=GpuTextureSystem.js.map