Skip to content

Commit

Permalink
fix(ext/webgpu): correctly validate GPUExtent3D, GPUOrigin3D, GPUOrig…
Browse files Browse the repository at this point in the history
…in2D & GPUColor (#23413)
  • Loading branch information
Hajime-san committed May 6, 2024
1 parent 4ab68df commit a635abb
Show file tree
Hide file tree
Showing 2 changed files with 301 additions and 1 deletion.
40 changes: 39 additions & 1 deletion ext/webgpu/01_webgpu.js
Original file line number Diff line number Diff line change
Expand Up @@ -1136,10 +1136,11 @@ class GPUDevice extends EventTarget {
"Argument 1",
);
const device = assertDevice(this, prefix, "this");
// assign normalized size to descriptor due to createGPUTexture needs it
descriptor.size = normalizeGPUExtent3D(descriptor.size);
const { rid, err } = op_webgpu_create_texture({
deviceRid: device.rid,
...descriptor,
size: normalizeGPUExtent3D(descriptor.size),
});
device.pushError(err);

Expand Down Expand Up @@ -5501,6 +5502,16 @@ webidl.converters["GPUExtent3D"] = (V, opts) => {
if (typeof V === "object") {
const method = V[SymbolIterator];
if (method !== undefined) {
// validate length of GPUExtent3D
const min = 1;
const max = 3;
if (V.length < min || V.length > max) {
throw webidl.makeException(
TypeError,
`A sequence of number used as a GPUExtent3D must have between ${min} and ${max} elements.`,
opts,
);
}
return webidl.converters["sequence<GPUIntegerCoordinate>"](V, opts);
}
return webidl.converters["GPUExtent3DDict"](V, opts);
Expand Down Expand Up @@ -6836,6 +6847,15 @@ webidl.converters["GPUOrigin3D"] = (V, opts) => {
if (typeof V === "object") {
const method = V[SymbolIterator];
if (method !== undefined) {
// validate length of GPUOrigin3D
const length = 3;
if (V.length > length) {
throw webidl.makeException(
TypeError,
`A sequence of number used as a GPUOrigin3D must have at most ${length} elements.`,
opts,
);
}
return webidl.converters["sequence<GPUIntegerCoordinate>"](V, opts);
}
return webidl.converters["GPUOrigin3DDict"](V, opts);
Expand Down Expand Up @@ -6904,6 +6924,15 @@ webidl.converters["GPUOrigin2D"] = (V, opts) => {
if (typeof V === "object") {
const method = V[SymbolIterator];
if (method !== undefined) {
// validate length of GPUOrigin2D
const length = 2;
if (V.length > length) {
throw webidl.makeException(
TypeError,
`A sequence of number used as a GPUOrigin2D must have at most ${length} elements.`,
opts,
);
}
return webidl.converters["sequence<GPUIntegerCoordinate>"](V, opts);
}
return webidl.converters["GPUOrigin2DDict"](V, opts);
Expand Down Expand Up @@ -6989,6 +7018,15 @@ webidl.converters["GPUColor"] = (V, opts) => {
if (typeof V === "object") {
const method = V[SymbolIterator];
if (method !== undefined) {
// validate length of GPUColor
const length = 4;
if (V.length !== length) {
throw webidl.makeException(
TypeError,
`A sequence of number used as a GPUColor must have exactly ${length} elements.`,
opts,
);
}
return webidl.converters["sequence<double>"](V, opts);
}
return webidl.converters["GPUColorDict"](V, opts);
Expand Down
262 changes: 262 additions & 0 deletions tests/unit/webgpu_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,268 @@ Deno.test(function getPreferredCanvasFormat() {
assert(preferredFormat === "bgra8unorm" || preferredFormat === "rgba8unorm");
});

Deno.test({
ignore: isWsl || isLinuxOrMacCI,
}, async function validateGPUColor() {
const adapter = await navigator.gpu.requestAdapter();
assert(adapter);
const device = await adapter.requestDevice();
assert(device);

const format = "rgba8unorm-srgb";
const encoder = device.createCommandEncoder();
const texture = device.createTexture({
size: [256, 256],
format,
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC,
});
const view = texture.createView();
const storeOp = "store";
const loadOp = "clear";

// values for validating GPUColor
const invalidSize = [0, 0, 0];

const msgIncludes =
"A sequence of number used as a GPUColor must have exactly 4 elements.";

// validate the argument of descriptor.colorAttachments[@@iterator].clearValue property's length of GPUCommandEncoder.beginRenderPass when its a sequence
// https://www.w3.org/TR/2024/WD-webgpu-20240409/#dom-gpucommandencoder-beginrenderpass
assertThrows(
() =>
encoder.beginRenderPass({
colorAttachments: [
{
view,
storeOp,
loadOp,
clearValue: invalidSize,
},
],
}),
TypeError,
msgIncludes,
);
const renderPass = encoder.beginRenderPass({
colorAttachments: [
{
view,
storeOp,
loadOp,
clearValue: [0, 0, 0, 1],
},
],
});
// validate the argument of color length of GPURenderPassEncoder.setBlendConstant when its a sequence
// https://www.w3.org/TR/2024/WD-webgpu-20240409/#dom-gpurenderpassencoder-setblendconstant
assertThrows(
() => renderPass.setBlendConstant(invalidSize),
TypeError,
msgIncludes,
);

device.destroy();
const resources = Object.keys(Deno.resources());
Deno.close(Number(resources[resources.length - 1]));
});

Deno.test({
ignore: isWsl || isLinuxOrMacCI,
}, async function validateGPUExtent3D() {
const adapter = await navigator.gpu.requestAdapter();
assert(adapter);
const device = await adapter.requestDevice();
assert(device);

const format = "rgba8unorm-srgb";
const encoder = device.createCommandEncoder();
const buffer = device.createBuffer({
size: new Uint32Array([1, 4, 3, 295]).byteLength,
usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST,
});
const usage = GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC;
const texture = device.createTexture({
size: [256, 256],
format,
usage,
});

// values for validating GPUExtent3D
const belowSize: Array<number> = [];
const overSize = [256, 256, 1, 1];

const msgIncludes =
"A sequence of number used as a GPUExtent3D must have between 1 and 3 elements.";

// validate the argument of descriptor.size property's length of GPUDevice.createTexture when its a sequence
// https://www.w3.org/TR/2024/WD-webgpu-20240409/#dom-gpudevice-createtexture
assertThrows(
() => device.createTexture({ size: belowSize, format, usage }),
TypeError,
msgIncludes,
);
assertThrows(
() => device.createTexture({ size: overSize, format, usage }),
TypeError,
msgIncludes,
);
// validate the argument of copySize property's length of GPUCommandEncoder.copyBufferToTexture when its a sequence
// https://www.w3.org/TR/2024/WD-webgpu-20240409/#dom-gpucommandencoder-copybuffertotexture
assertThrows(
() => encoder.copyBufferToTexture({ buffer }, { texture }, belowSize),
TypeError,
msgIncludes,
);
assertThrows(
() => encoder.copyBufferToTexture({ buffer }, { texture }, overSize),
TypeError,
msgIncludes,
);
// validate the argument of copySize property's length of GPUCommandEncoder.copyTextureToBuffer when its a sequence
// https://www.w3.org/TR/2024/WD-webgpu-20240409/#dom-gpucommandencoder-copytexturetobuffer
assertThrows(
() => encoder.copyTextureToBuffer({ texture }, { buffer }, belowSize),
TypeError,
msgIncludes,
);
assertThrows(
() => encoder.copyTextureToBuffer({ texture }, { buffer }, overSize),
TypeError,
msgIncludes,
);
// validate the argument of copySize property's length of GPUCommandEncoder.copyTextureToTexture when its a sequence
// https://www.w3.org/TR/2024/WD-webgpu-20240409/#dom-gpucommandencoder-copytexturetotexture
assertThrows(
() => encoder.copyTextureToTexture({ texture }, { texture }, belowSize),
TypeError,
msgIncludes,
);
assertThrows(
() => encoder.copyTextureToTexture({ texture }, { texture }, overSize),
TypeError,
msgIncludes,
);
const data = new Uint8Array([1 * 255, 1 * 255, 1 * 255, 1 * 255]);
// validate the argument of size property's length of GPUQueue.writeTexture when its a sequence
// https://www.w3.org/TR/2024/WD-webgpu-20240409/#dom-gpuqueue-writetexture
assertThrows(
() => device.queue.writeTexture({ texture }, data, {}, belowSize),
TypeError,
msgIncludes,
);
assertThrows(
() => device.queue.writeTexture({ texture }, data, {}, overSize),
TypeError,
msgIncludes,
);
// NOTE: GPUQueue.copyExternalImageToTexture needs to be validated the argument of copySize property's length when its a sequence, but it is not implemented yet

device.destroy();
const resources = Object.keys(Deno.resources());
Deno.close(Number(resources[resources.length - 1]));
});

Deno.test({
ignore: true,
}, async function validateGPUOrigin2D() {
// NOTE: GPUQueue.copyExternalImageToTexture needs to be validated the argument of source.origin property's length when its a sequence, but it is not implemented yet
});

Deno.test({
ignore: isWsl || isLinuxOrMacCI,
}, async function validateGPUOrigin3D() {
const adapter = await navigator.gpu.requestAdapter();
assert(adapter);
const device = await adapter.requestDevice();
assert(device);

const format = "rgba8unorm-srgb";
const encoder = device.createCommandEncoder();
const buffer = device.createBuffer({
size: new Uint32Array([1, 4, 3, 295]).byteLength,
usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST,
});
const usage = GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC;
const size = [256, 256, 1];
const texture = device.createTexture({
size,
format,
usage,
});

// value for validating GPUOrigin3D
const overSize = [256, 256, 1, 1];

const msgIncludes =
"A sequence of number used as a GPUOrigin3D must have at most 3 elements.";

// validate the argument of destination.origin property's length of GPUCommandEncoder.copyBufferToTexture when its a sequence
// https://www.w3.org/TR/2024/WD-webgpu-20240409/#dom-gpucommandencoder-copybuffertotexture
assertThrows(
() =>
encoder.copyBufferToTexture(
{ buffer },
{ texture, origin: overSize },
size,
),
TypeError,
msgIncludes,
);
// validate the argument of source.origin property's length of GPUCommandEncoder.copyTextureToBuffer when its a sequence
// https://www.w3.org/TR/2024/WD-webgpu-20240409/#dom-gpucommandencoder-copytexturetobuffer
assertThrows(
() =>
encoder.copyTextureToBuffer(
{ texture, origin: overSize },
{ buffer },
size,
),
TypeError,
msgIncludes,
);
// validate the argument of source.origin property's length of GPUCommandEncoder.copyTextureToTexture when its a sequence
// https://www.w3.org/TR/2024/WD-webgpu-20240409/#dom-gpucommandencoder-copytexturetotexture
assertThrows(
() =>
encoder.copyTextureToTexture(
{ texture, origin: overSize },
{ texture },
size,
),
TypeError,
msgIncludes,
);
// validate the argument of destination.origin property's length of GPUCommandEncoder.copyTextureToTexture when its a sequence
assertThrows(
() =>
encoder.copyTextureToTexture(
{ texture },
{ texture, origin: overSize },
size,
),
TypeError,
msgIncludes,
);
// validate the argument of destination.origin property's length of GPUQueue.writeTexture when its a sequence
// https://www.w3.org/TR/2024/WD-webgpu-20240409/#dom-gpuqueue-writetexture
assertThrows(
() =>
device.queue.writeTexture(
{ texture, origin: overSize },
new Uint8Array([1 * 255, 1 * 255, 1 * 255, 1 * 255]),
{},
size,
),
TypeError,
msgIncludes,
);
// NOTE: GPUQueue.copyExternalImageToTexture needs to be validated the argument of destination.origin property's length when its a sequence, but it is not implemented yet

device.destroy();
const resources = Object.keys(Deno.resources());
Deno.close(Number(resources[resources.length - 1]));
});

async function checkIsWsl() {
return Deno.build.os === "linux" && await hasMicrosoftProcVersion();

Expand Down

0 comments on commit a635abb

Please sign in to comment.