Skip to content

Commit

Permalink
Fix readpixel (#130)
Browse files Browse the repository at this point in the history
* fix: read texture in webgpu

* chore: commit changeset
  • Loading branch information
xiaoiver authored Jan 2, 2024
1 parent c0434c8 commit 05e33ac
Show file tree
Hide file tree
Showing 7 changed files with 364 additions and 27 deletions.
5 changes: 5 additions & 0 deletions .changeset/late-moons-help.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@antv/g-device-api': patch
---

Read texture in webgpu.
291 changes: 291 additions & 0 deletions examples/demos/canvas-gradient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,291 @@
import {
DeviceContribution,
VertexStepMode,
Format,
TransparentWhite,
BufferUsage,
BufferFrequencyHint,
BlendMode,
BlendFactor,
TextureUsage,
CullMode,
ChannelWriteMask,
TransparentBlack,
CompareFunction,
AddressMode,
FilterMode,
MipmapFilterMode,
} from '../../src';
import { vec3, mat4 } from 'gl-matrix';
import {
cubeVertexArray,
cubeVertexSize,
cubeVertexCount,
cubePositionOffset,
cubeUVOffset,
} from '../meshes/cube';
import { generateColorRamp } from '../utils/gradient';

export async function render(
deviceContribution: DeviceContribution,
$canvas: HTMLCanvasElement,
useRAF = true,
image?: HTMLImageElement,
) {
// create swap chain and get device
const swapChain = await deviceContribution.createSwapChain($canvas);

// TODO: resize
swapChain.configureSwapChain($canvas.width, $canvas.height);
const device = swapChain.getDevice();

const ramp = generateColorRamp({
colors: [
'#FF4818',
'#F7B74A',
'#FFF598',
'#91EABC',
'#2EA9A1',
'#206C7C',
].reverse(),
positions: [0, 0.2, 0.4, 0.6, 0.8, 1.0],
});
const gradientTexture = device.createTexture({
format: Format.U8_RGBA_NORM,
width: ramp.width,
height: ramp.height,
usage: TextureUsage.SAMPLED,
});
device.setResourceName(gradientTexture, 'Gradient Texture');
gradientTexture.setImageData([ramp.data]);

const program = device.createProgram({
vertex: {
glsl: `
layout(std140) uniform Uniforms {
mat4 u_ModelViewProjectionMatrix;
float u_Test;
};
layout(location = 0) in vec4 a_Position;
layout(location = 1) in vec2 a_Uv;
out vec2 v_Uv;
void main() {
v_Uv = a_Uv;
gl_Position = u_ModelViewProjectionMatrix * a_Position;
}
`,
},
fragment: {
glsl: `
uniform sampler2D u_Texture;
in vec2 v_Uv;
out vec4 outputColor;
void main() {
outputColor = texture(SAMPLER_2D(u_Texture), v_Uv);
}
`,
},
});

const vertexBuffer = device.createBuffer({
viewOrSize: cubeVertexArray,
usage: BufferUsage.VERTEX,
});

const uniformBuffer = device.createBuffer({
viewOrSize: 16 * 4 + 4 * 4, // mat4
usage: BufferUsage.UNIFORM,
hint: BufferFrequencyHint.DYNAMIC,
});

const sampler = device.createSampler({
addressModeU: AddressMode.CLAMP_TO_EDGE,
addressModeV: AddressMode.CLAMP_TO_EDGE,
minFilter: FilterMode.POINT,
magFilter: FilterMode.BILINEAR,
mipmapFilter: MipmapFilterMode.LINEAR,
lodMinClamp: 0,
lodMaxClamp: 0,
});

const inputLayout = device.createInputLayout({
vertexBufferDescriptors: [
{
arrayStride: cubeVertexSize,
stepMode: VertexStepMode.VERTEX,
attributes: [
{
shaderLocation: 0,
offset: cubePositionOffset,
format: Format.F32_RGBA,
},
{
shaderLocation: 1,
offset: cubeUVOffset,
format: Format.F32_RG,
},
],
},
],
indexBufferFormat: null,
program,
});

const pipeline = device.createRenderPipeline({
inputLayout,
program,
colorAttachmentFormats: [Format.U8_RGBA_RT],
depthStencilAttachmentFormat: Format.D24_S8,
megaStateDescriptor: {
attachmentsState: [
{
channelWriteMask: ChannelWriteMask.ALL,
rgbBlendState: {
blendMode: BlendMode.ADD,
blendSrcFactor: BlendFactor.SRC_ALPHA,
blendDstFactor: BlendFactor.ONE_MINUS_SRC_ALPHA,
},
alphaBlendState: {
blendMode: BlendMode.ADD,
blendSrcFactor: BlendFactor.ONE,
blendDstFactor: BlendFactor.ONE_MINUS_SRC_ALPHA,
},
},
],
blendConstant: TransparentBlack,
depthWrite: true,
depthCompare: CompareFunction.LESS,
cullMode: CullMode.BACK,
stencilWrite: false,
},
});

const bindings = device.createBindings({
pipeline,
uniformBufferBindings: [
{
binding: 0,
buffer: uniformBuffer,
},
],
samplerBindings: [
{
texture: gradientTexture,
sampler,
},
],
});

const mainColorRT = device.createRenderTargetFromTexture(
device.createTexture({
format: Format.U8_RGBA_RT,
width: $canvas.width,
height: $canvas.height,
usage: TextureUsage.RENDER_TARGET,
}),
);
const mainDepthRT = device.createRenderTargetFromTexture(
device.createTexture({
format: Format.D24_S8,
width: $canvas.width,
height: $canvas.height,
usage: TextureUsage.RENDER_TARGET,
}),
);

let id: number;
const frame = () => {
const aspect = $canvas.width / $canvas.height;
const projectionMatrix = mat4.perspective(
mat4.create(),
(2 * Math.PI) / 5,
aspect,
0.1,
1000,
);
const viewMatrix = mat4.identity(mat4.create());
const modelViewProjectionMatrix = mat4.create();
mat4.translate(viewMatrix, viewMatrix, vec3.fromValues(0, 0, -4));
const now = useRAF ? Date.now() / 1000 : 0;
mat4.rotate(
viewMatrix,
viewMatrix,
1,
vec3.fromValues(Math.sin(now), Math.cos(now), 0),
);
mat4.multiply(modelViewProjectionMatrix, projectionMatrix, viewMatrix);
uniformBuffer.setSubData(
0,
new Uint8Array((modelViewProjectionMatrix as Float32Array).buffer),
);
// WebGL1 need this
program.setUniformsLegacy({
u_ModelViewProjectionMatrix: modelViewProjectionMatrix,
u_Texture: gradientTexture,
});

/**
* An application should call getCurrentTexture() in the same task that renders to the canvas texture.
* Otherwise, the texture could get destroyed by these steps before the application is finished rendering to it.
*/
const onscreenTexture = swapChain.getOnscreenTexture();

const renderPass = device.createRenderPass({
colorAttachment: [mainColorRT],
colorResolveTo: [onscreenTexture],
colorClearColor: [TransparentWhite],
depthStencilAttachment: mainDepthRT,
depthClearValue: 1,
});

renderPass.setPipeline(pipeline);
renderPass.setVertexInput(
inputLayout,
[
{
buffer: vertexBuffer,
},
],
null,
);
renderPass.setViewport(0, 0, $canvas.width, $canvas.height);
renderPass.setBindings(bindings);
renderPass.draw(cubeVertexCount);

device.submitPass(renderPass);
if (useRAF) {
id = requestAnimationFrame(frame);
}
};

frame();

return () => {
if (useRAF && id) {
cancelAnimationFrame(id);
}
program.destroy();
vertexBuffer.destroy();
uniformBuffer.destroy();
inputLayout.destroy();
bindings.destroy();
pipeline.destroy();
mainColorRT.destroy();
mainDepthRT.destroy();

sampler.destroy();
device.destroy();

// For debug.
device.checkForLeaks();
};
}

render.params = {
targets: ['webgl1', 'webgl2', 'webgpu'],
default: 'webgpu',
};
1 change: 1 addition & 0 deletions examples/demos/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export { render as MSAA } from './msaa';
export { render as MultipleRenderTargets } from './multiple-render-targets';
export { render as MultipleRenderPasses } from './multiple-render-passes';
export { render as Blit } from './blit';
export { render as CanvasGradient } from './canvas-gradient';
export { render as RotatingCube } from './rotating-cube';
export { render as NestedRenderPass } from './nested-render-pass';
export { render as Stencil } from './stencil';
Expand Down
8 changes: 7 additions & 1 deletion examples/demos/read-pixel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,13 @@ void main() {
const row = width * 4;
const end = (height - 1) * row;
for (let i = 0; i < length; i += row) {
ci.data.set(data.subarray(i, i + row), i);
const r = data.subarray(i, i + row); // bgra
// for (let j = 0; j < row; j += 4) {
// const t = r[j];
// r[j] = r[j + 2];
// r[j + 2] = t;
// }
ci.data.set(r, i);
}
context.putImageData(ci, 0, 0);

Expand Down
6 changes: 4 additions & 2 deletions examples/demos/render-to-texture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,15 +86,16 @@ void main() {
const trianglePipeline = device.createRenderPipeline({
inputLayout: triangleInputLayout,
program: triangleProgram,
colorAttachmentFormats: [Format.U8_RGBA_RT],
colorAttachmentFormats: [Format.U8_RGBA_NORM],
});

const triangleTexture = device.createTexture({
format: Format.U8_RGBA_RT,
format: Format.U8_RGBA_NORM,
width: $canvas.width,
height: $canvas.height,
usage: TextureUsage.RENDER_TARGET,
});
device.setResourceName(triangleTexture, 'Triangle Texture');
const triangleRenderTarget =
device.createRenderTargetFromTexture(triangleTexture);

Expand Down Expand Up @@ -277,6 +278,7 @@ void main() {
colorAttachment: [triangleRenderTarget],
colorResolveTo: [null],
colorClearColor: [TransparentWhite],
colorStore: [true],
depthStencilAttachment: null,
depthStencilResolveTo: null,
});
Expand Down
26 changes: 26 additions & 0 deletions examples/utils/gradient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export function generateColorRamp(colorRamp: any): any {
let canvas = window.document.createElement('canvas');
let ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
canvas.width = 256;
canvas.height = 1;
let data: Uint8ClampedArray | undefined = undefined;

// draw linear color
const gradient = ctx.createLinearGradient(0, 0, 256, 1);

const min = colorRamp.positions[0];
const max = colorRamp.positions[colorRamp.positions.length - 1];
for (let i = 0; i < colorRamp.colors.length; ++i) {
const value = (colorRamp.positions[i] - min) / (max - min);
gradient.addColorStop(value, colorRamp.colors[i]);
}
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, 256, 1);

data = new Uint8ClampedArray(ctx.getImageData(0, 0, 256, 1).data);
// @ts-ignore
canvas = null;
// @ts-ignore
ctx = null;
return { data, width: 256, height: 1 };
}
Loading

0 comments on commit 05e33ac

Please sign in to comment.