pixi.js
Version:
<p align="center"> <a href="https://pixijs.com" target="_blank" rel="noopener noreferrer"> <img height="150" src="https://files.pixijs.download/branding/pixijs-logo-transparent-dark.svg?v=1" alt="PixiJS logo"> </a> </p> <br/> <p align="center">
159 lines (152 loc) • 5.54 kB
JavaScript
'use strict';
"use strict";
class GpuMipmapGenerator {
constructor(device) {
this.device = device;
this.sampler = device.createSampler({ minFilter: "linear" });
this.pipelines = {};
}
_getMipmapPipeline(format) {
let pipeline = this.pipelines[format];
if (!pipeline) {
if (!this.mipmapShaderModule) {
this.mipmapShaderModule = this.device.createShaderModule({
code: (
/* wgsl */
`
var<private> pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
vec2<f32>(-1.0, -1.0), vec2<f32>(-1.0, 3.0), vec2<f32>(3.0, -1.0));
struct VertexOutput {
position : vec4<f32>,
texCoord : vec2<f32>,
};
fn vertexMain( vertexIndex : u32) -> VertexOutput {
var output : VertexOutput;
output.texCoord = pos[vertexIndex] * vec2<f32>(0.5, -0.5) + vec2<f32>(0.5);
output.position = vec4<f32>(pos[vertexIndex], 0.0, 1.0);
return output;
}
var imgSampler : sampler;
var img : texture_2d<f32>;
fn fragmentMain( texCoord : vec2<f32>) -> vec4<f32> {
return textureSample(img, imgSampler, texCoord);
}
`
)
});
}
pipeline = this.device.createRenderPipeline({
layout: "auto",
vertex: {
module: this.mipmapShaderModule,
entryPoint: "vertexMain"
},
fragment: {
module: this.mipmapShaderModule,
entryPoint: "fragmentMain",
targets: [{ format }]
}
});
this.pipelines[format] = pipeline;
}
return pipeline;
}
/**
* Generates mipmaps for the given GPUTexture from the data in level 0.
* @param {module:External.GPUTexture} texture - Texture to generate mipmaps for.
* @returns {module:External.GPUTexture} - The originally passed texture
*/
generateMipmap(texture) {
const pipeline = this._getMipmapPipeline(texture.format);
if (texture.dimension === "3d" || texture.dimension === "1d") {
throw new Error("Generating mipmaps for non-2d textures is currently unsupported!");
}
let mipTexture = texture;
const arrayLayerCount = texture.depthOrArrayLayers || 1;
const renderToSource = texture.usage & GPUTextureUsage.RENDER_ATTACHMENT;
if (!renderToSource) {
const mipTextureDescriptor = {
size: {
width: Math.ceil(texture.width / 2),
height: Math.ceil(texture.height / 2),
depthOrArrayLayers: arrayLayerCount
},
format: texture.format,
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_SRC | GPUTextureUsage.RENDER_ATTACHMENT,
mipLevelCount: texture.mipLevelCount - 1
};
mipTexture = this.device.createTexture(mipTextureDescriptor);
}
const commandEncoder = this.device.createCommandEncoder({});
const bindGroupLayout = pipeline.getBindGroupLayout(0);
for (let arrayLayer = 0; arrayLayer < arrayLayerCount; ++arrayLayer) {
let srcView = texture.createView({
baseMipLevel: 0,
mipLevelCount: 1,
dimension: "2d",
baseArrayLayer: arrayLayer,
arrayLayerCount: 1
});
let dstMipLevel = renderToSource ? 1 : 0;
for (let i = 1; i < texture.mipLevelCount; ++i) {
const dstView = mipTexture.createView({
baseMipLevel: dstMipLevel++,
mipLevelCount: 1,
dimension: "2d",
baseArrayLayer: arrayLayer,
arrayLayerCount: 1
});
const passEncoder = commandEncoder.beginRenderPass({
colorAttachments: [{
view: dstView,
storeOp: "store",
loadOp: "clear",
clearValue: { r: 0, g: 0, b: 0, a: 0 }
}]
});
const bindGroup = this.device.createBindGroup({
layout: 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: Math.ceil(texture.width / 2),
height: Math.ceil(texture.height / 2),
depthOrArrayLayers: arrayLayerCount
};
for (let i = 1; i < texture.mipLevelCount; ++i) {
commandEncoder.copyTextureToTexture({
texture: mipTexture,
mipLevel: i - 1
}, {
texture,
mipLevel: i
}, mipLevelSize);
mipLevelSize.width = Math.ceil(mipLevelSize.width / 2);
mipLevelSize.height = Math.ceil(mipLevelSize.height / 2);
}
}
this.device.queue.submit([commandEncoder.finish()]);
if (!renderToSource) {
mipTexture.destroy();
}
return texture;
}
}
exports.GpuMipmapGenerator = GpuMipmapGenerator;
//# sourceMappingURL=GpuMipmapGenerator.js.map