Skip to content

Commit

Permalink
fix: fixed binding group layout validation error (#207) (#210)
Browse files Browse the repository at this point in the history
* fix: fixed binding group layout validation error (#207)

WebGPU does not allow automatically inferred layouts to be shared between different pipelines

* feat: mipmap generator (#209)

* fix: fixed error when render target and swap chain height don't match (#208)

---------

Co-authored-by: RE <[email protected]>
  • Loading branch information
xiaoiver and re-ovo authored Dec 12, 2024
1 parent f5aea08 commit a1584fc
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 1 deletion.
1 change: 1 addition & 0 deletions src/api/utils/hash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ export function bindingsDescriptorEquals(
b.storageBufferBindings = b.storageBufferBindings || [];
b.storageTextureBindings = b.storageTextureBindings || [];

if (a.pipeline !== b.pipeline) return false;
if (a.samplerBindings.length !== b.samplerBindings.length) return false;
if (!arrayEqual(a.samplerBindings, b.samplerBindings, samplerBindingEquals))
return false;
Expand Down
7 changes: 7 additions & 0 deletions src/webgpu/Device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ import {
} from './utils';
import { preprocessShader_GLSL } from '../shader';
import { RenderBundle_WebGPU } from './RenderBundle';
import { MipmapGenerator } from './MipmapGenerator';

export class Device_WebGPU implements SwapChain, IDevice_WebGPU {
private swapChainWidth = 0;
Expand Down Expand Up @@ -123,6 +124,7 @@ export class Device_WebGPU implements SwapChain, IDevice_WebGPU {
private canvasContext: GPUCanvasContext;
private glsl_compile: typeof glsl_compile_;
private WGSLComposer: WGSLComposer;
private mipmapGenerator: MipmapGenerator;

constructor(
adapter: GPUAdapter,
Expand All @@ -137,6 +139,7 @@ export class Device_WebGPU implements SwapChain, IDevice_WebGPU {
this.canvasContext = canvasContext;
this.glsl_compile = glsl_compile;
this.WGSLComposer = wGSLComposer;
this.mipmapGenerator = new MipmapGenerator(device);

this.fallbackTexture2D = this.createFallbackTexture(
TextureDimension.TEXTURE_2D,
Expand Down Expand Up @@ -266,6 +269,10 @@ export class Device_WebGPU implements SwapChain, IDevice_WebGPU {
return this;
}

getMipmapGenerator(): MipmapGenerator {
return this.mipmapGenerator;
}

getCanvas(): HTMLCanvasElement | OffscreenCanvas {
return this.canvas;
}
Expand Down
127 changes: 127 additions & 0 deletions src/webgpu/MipmapGenerator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
export class MipmapGenerator {
private readonly _device: GPUDevice;
private readonly _mipmapShader: GPUShaderModule;
private readonly _mipmapSampler: GPUSampler;
private readonly _pipelines: Map<GPUTextureFormat, GPURenderPipeline>;

constructor(device: GPUDevice) {
this._device = device;

this._mipmapShader = this._device.createShaderModule({
label: 'MipmapGenerator',
code: `
struct VSOutput {
@builtin(position) position: vec4f,
@location(0) texcoord: vec2f,
};
@vertex fn vs(
@builtin(vertex_index) vertexIndex : u32
) -> VSOutput {
let pos = array(
// 1st triangle
vec2f( 0.0, 0.0), // center
vec2f( 1.0, 0.0), // right, center
vec2f( 0.0, 1.0), // center, top
// 2nd triangle
vec2f( 0.0, 1.0), // center, top
vec2f( 1.0, 0.0), // right, center
vec2f( 1.0, 1.0), // right, top
);
var vsOutput: VSOutput;
let xy = pos[vertexIndex];
vsOutput.position = vec4f(xy * 2.0 - 1.0, 0.0, 1.0);
vsOutput.texcoord = vec2f(xy.x, 1.0 - xy.y);
return vsOutput;
}
@group(0) @binding(0) var ourSampler: sampler;
@group(0) @binding(1) var ourTexture: texture_2d<f32>;
@fragment fn fs(fsInput: VSOutput) -> @location(0) vec4f {
return textureSample(ourTexture, ourSampler, fsInput.texcoord);
}
`,
});
this._mipmapSampler = this._device.createSampler({
minFilter: 'linear',
});
this._pipelines = new Map();
}

private _requestPipeline(format: GPUTextureFormat) {
let pipeline = this._pipelines.get(format);
if (!pipeline) {
pipeline = this._device.createRenderPipeline({
layout: 'auto',
vertex: {
module: this._mipmapShader,
entryPoint: 'vs',
},
fragment: {
module: this._mipmapShader,
entryPoint: 'fs',
targets: [{ format }],
},
});
this._pipelines.set(format, pipeline);
}
return pipeline;
}

generateMipmap(texture: GPUTexture) {
const commandEncoder = this._device.createCommandEncoder({
label: 'mipmap generator command encoder',
});
const pipeline = this._requestPipeline(texture.format,);

let width = texture.width;
let height = texture.height;
let baseMipLevel = 0;

while ((width > 1 || height > 1) && baseMipLevel < texture.mipLevelCount - 1) {
width = Math.max(1, width / 2);
height = Math.max(1, height / 2);

const bindGroup = this._device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [
{
binding: 0,
resource: this._mipmapSampler,
},
{
binding: 1,
resource: texture.createView({
baseMipLevel,
mipLevelCount: 1,
}),
},
],
});

++baseMipLevel;

const pass = commandEncoder.beginRenderPass({
colorAttachments: [
{
view: texture.createView({
baseMipLevel,
mipLevelCount: 1,
}),
loadOp: 'clear',
storeOp: 'store',
},
],
});
pass.setPipeline(pipeline);
pass.setBindGroup(0, bindGroup);
pass.draw(6);
pass.end();
}

this._device.queue.submit([commandEncoder.finish()]);
}
}
2 changes: 1 addition & 1 deletion src/webgpu/RenderPass.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ export class RenderPass_WebGPU implements RenderPass {
}

private flipY(y: number, h: number) {
const height = this.device['swapChainHeight'];
const height = this.gfxColorAttachment[0].height;
return height - y - h;
}

Expand Down
6 changes: 6 additions & 0 deletions src/webgpu/Texture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
getBlockInformationFromFormat,
translateTextureViewDimension,
} from './utils';
import { Device_WebGPU } from './Device';

export class Texture_WebGPU
extends ResourceBase_WebGPU
Expand Down Expand Up @@ -86,6 +87,7 @@ export class Texture_WebGPU
GPUTextureUsage.TEXTURE_BINDING |
GPUTextureUsage.COPY_DST |
GPUTextureUsage.RENDER_ATTACHMENT,
mipLevelCount: this.mipLevelCount,
};
const texture = device.createTexture(textureDescriptor);

Expand All @@ -97,6 +99,10 @@ export class Texture_WebGPU
);
}

if(this.mipLevelCount > 1) {
(this.device as Device_WebGPU).getMipmapGenerator().generateMipmap(texture);
}

return [texture, width, height];
}

Expand Down

0 comments on commit a1584fc

Please sign in to comment.