Auto merge of #27614 - kunalmohan:webgpu-cts, r=kvark

Minor fixes and update cts

<!-- Please describe your changes on the following line: -->
- Prevent redundant buffer and texture destroy calls.
- More subtests for B2B copy pass now.
- All tests under `setViewport()` and `setScissorRect()` pass now.
- Tests for `createTexture()` do not crash. More than 50% of them pass now.

r?@kvark

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: -->
- [X] `./mach build -d` does not report any errors
- [X] `./mach test-tidy` does not report any errors
- [ ] These changes fix #___ (GitHub issue number if applicable)

<!-- Either: -->
- [X] There are tests for these changes OR
- [ ] These changes do not require tests because ___

<!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.-->

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->
This commit is contained in:
bors-servo 2020-08-27 13:12:27 -04:00 committed by GitHub
commit 9e6da58d77
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 1407 additions and 98 deletions

4
Cargo.lock generated
View file

@ -7028,7 +7028,7 @@ dependencies = [
[[package]]
name = "wgpu-core"
version = "0.6.0"
source = "git+https://github.com/gfx-rs/wgpu#1d0e0ce37ede5ec53000ab252c27b8cf856865b2"
source = "git+https://github.com/gfx-rs/wgpu#59f0996eabd43e882d4bfc73ee5b4ed912617abf"
dependencies = [
"arrayvec 0.5.1",
"bitflags",
@ -7055,7 +7055,7 @@ dependencies = [
[[package]]
name = "wgpu-types"
version = "0.6.0"
source = "git+https://github.com/gfx-rs/wgpu#1d0e0ce37ede5ec53000ab252c27b8cf856865b2"
source = "git+https://github.com/gfx-rs/wgpu#59f0996eabd43e882d4bfc73ee5b4ed912617abf"
dependencies = [
"bitflags",
"serde",

View file

@ -184,6 +184,7 @@ impl GPUBufferMethods for GPUBuffer {
GPUBufferState::Mapped | GPUBufferState::MappedAtCreation => {
self.Unmap();
},
GPUBufferState::Destroyed => return,
_ => {},
};
if let Err(e) = self

View file

@ -18,6 +18,7 @@ use crate::dom::gpudevice::{
};
use crate::dom::gputextureview::GPUTextureView;
use dom_struct::dom_struct;
use std::cell::Cell;
use std::num::NonZeroU32;
use std::string::String;
use webgpu::{
@ -40,6 +41,7 @@ pub struct GPUTexture {
dimension: GPUTextureDimension,
format: GPUTextureFormat,
texture_usage: u32,
destroyed: Cell<bool>,
}
impl GPUTexture {
@ -67,6 +69,7 @@ impl GPUTexture {
dimension,
format,
texture_usage,
destroyed: Cell::new(false),
}
}
@ -197,6 +200,9 @@ impl GPUTextureMethods for GPUTexture {
/// https://gpuweb.github.io/gpuweb/#dom-gputexture-destroy
fn Destroy(&self) {
if self.destroyed.get() {
return;
}
if let Err(e) = self
.channel
.0
@ -207,5 +213,6 @@ impl GPUTextureMethods for GPUTexture {
self.texture.0, e
);
};
self.destroyed.set(true);
}
}

View file

@ -21,7 +21,6 @@ use servo_config::pref;
use smallvec::SmallVec;
use std::borrow::Cow;
use std::cell::RefCell;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::num::NonZeroU64;
use std::rc::Rc;
@ -475,9 +474,7 @@ impl<'a> WGPU<'a> {
))
.map_err(|e| format!("{:?}", e))
};
if result.is_err() {
self.encoder_record_error(command_encoder_id, result.clone());
}
self.encoder_record_error(command_encoder_id, &result);
self.send_result(device_id, scope_id, result);
},
WebGPURequest::CopyBufferToBuffer {
@ -497,7 +494,7 @@ impl<'a> WGPU<'a> {
destination_offset,
size
));
self.encoder_record_error(command_encoder_id, result);
self.encoder_record_error(command_encoder_id, &result);
},
WebGPURequest::CopyBufferToTexture {
command_encoder_id,
@ -512,7 +509,7 @@ impl<'a> WGPU<'a> {
&destination,
&copy_size
));
self.encoder_record_error(command_encoder_id, result);
self.encoder_record_error(command_encoder_id, &result);
},
WebGPURequest::CopyTextureToBuffer {
command_encoder_id,
@ -527,7 +524,7 @@ impl<'a> WGPU<'a> {
&destination,
&copy_size
));
self.encoder_record_error(command_encoder_id, result);
self.encoder_record_error(command_encoder_id, &result);
},
WebGPURequest::CopyTextureToTexture {
command_encoder_id,
@ -542,7 +539,7 @@ impl<'a> WGPU<'a> {
&destination,
&copy_size
));
self.encoder_record_error(command_encoder_id, result);
self.encoder_record_error(command_encoder_id, &result);
},
WebGPURequest::CreateBindGroup {
device_id,
@ -985,7 +982,7 @@ impl<'a> WGPU<'a> {
} else {
Err(String::from("Invalid ComputePass"))
};
self.encoder_record_error(command_encoder_id, result);
self.encoder_record_error(command_encoder_id, &result);
},
WebGPURequest::RunRenderPass {
command_encoder_id,
@ -1000,7 +997,7 @@ impl<'a> WGPU<'a> {
} else {
Err(String::from("Invalid RenderPass"))
};
self.encoder_record_error(command_encoder_id, result);
self.encoder_record_error(command_encoder_id, &result);
},
WebGPURequest::Submit {
queue_id,
@ -1279,12 +1276,13 @@ impl<'a> WGPU<'a> {
fn encoder_record_error<U, T: std::fmt::Debug>(
&self,
encoder_id: id::CommandEncoderId,
result: Result<U, T>,
result: &Result<U, T>,
) {
if let Err(e) = result {
if let Entry::Vacant(v) = self.error_command_encoders.borrow_mut().entry(encoder_id) {
v.insert(format!("{:?}", e));
}
if let Err(ref e) = result {
self.error_command_encoders
.borrow_mut()
.entry(encoder_id)
.or_insert_with(|| format!("{:?}", e));
}
}
}

View file

@ -89,7 +89,7 @@
[]
],
"params_utils.js": [
"a95d01b9c8058076af5cf49d4ab82b5c74367a5b",
"d4ffd25372d2d3e975c683ca8a17c2c82d5c8687",
[]
],
"query": {
@ -98,7 +98,7 @@
[]
],
"encode_selectively.js": [
"62cb55ee039b7cc04dfdf751ce2241ba5864cac6",
"cb365deb4bb688e8ae5eebdef24a2ffcb3d857ef",
[]
],
"json_param_value.js": [
@ -106,7 +106,7 @@
[]
],
"parseQuery.js": [
"6c10baab0b2f97dc09e633201dae6618842fbd69",
"8bfd88bc9b66a41e08b3283b13b65bb3da0c10fe",
[]
],
"query.js": [
@ -161,7 +161,7 @@
]
},
"version.js": [
"794e3cdef44214801f15234a61aaf5af338f97fc",
"74eef63b8a1fa6042d1d8352b25eaf95d4b25985",
[]
]
},
@ -223,6 +223,10 @@
]
}
},
"copyBetweenLinearDataAndTexture.spec.js": [
"d2b89189e2c65d50f4f6f0c20f32a7f6512c5b35",
[]
],
"fences.spec.js": [
"98f913008b8af33e1dc866f5714388d4ec9e050d",
[]
@ -311,7 +315,7 @@
],
"resource_usages": {
"textureUsageInRender.spec.js": [
"12efa65ce2d9e9ed67cb2be398a0466424d38015",
"b036245663df8d684067eff7c79861253205dbca",
[]
]
},
@ -324,7 +328,7 @@
[]
],
"setScissorRect.spec.js": [
"7934c007286c8c4e9702eaaadb14b288a78e8fb1",
"c049e92e70e3ad9a5f71f88a863e5a86cc74fc4e",
[]
],
"setStencilReference.spec.js": [
@ -350,7 +354,7 @@
[]
],
"gpu_test.js": [
"f350131af3babe9729dec35261f7c445e2bf41d2",
"21cb10f1429e495b2f32b1d51a09b8584f63707d",
[]
],
"idl": {
@ -366,7 +370,7 @@
]
},
"listing.js": [
"3134cf0dd5693705ed82f0828f4dbd93fedf358e",
"cbb23b30ec894c103a957f944b18be1e019ed571",
[]
],
"util": {
@ -380,7 +384,7 @@
],
"texture": {
"layout.js": [
"c3c610bf0d93ebe66abf81d8f2017deba62641a3",
"927798985fc772a4c9e9ea7a62335ab693b43991",
[]
],
"subresource.js": [
@ -435,7 +439,7 @@
"testharness": {
"webgpu": {
"cts.html": [
"28d69b38d20367b4c61e72e834f518a11e8de411",
"63357f7e996ecadde32f6816dc131b94e9ab976c",
[
"webgpu/cts.html?q=webgpu:api,operation,buffers,map_detach:*",
{}
@ -456,6 +460,10 @@
"webgpu/cts.html?q=webgpu:api,operation,command_buffer,render,basic:*",
{}
],
[
"webgpu/cts.html?q=webgpu:api,operation,copyBetweenLinearDataAndTexture:*",
{}
],
[
"webgpu/cts.html?q=webgpu:api,operation,fences:*",
{}
@ -484,10 +492,6 @@
"webgpu/cts.html?q=webgpu:api,validation,createPipelineLayout:*",
{}
],
[
"webgpu/cts.html?q=webgpu:api,validation,createTexture:*",
{}
],
[
"webgpu/cts.html?q=webgpu:api,validation,error_scope:*",
{}

View file

@ -30,36 +30,95 @@
[cts.html?q=webgpu:api,validation,createTexture:*]
expected: CRASH
[webgpu:api,validation,createTexture:it_is_invalid_to_have_an_output_attachment_texture_with_non_renderable_format:format="bc2-rgba-unorm"]
expected: FAIL
[webgpu:api,validation,createTexture:it_is_invalid_to_have_an_output_attachment_texture_with_non_renderable_format:format="bc3-rgba-unorm"]
expected: FAIL
[webgpu:api,validation,createTexture:validation_of_mipLevelCount:width=32;height=31;mipLevelCount=7]
expected: FAIL
[webgpu:api,validation,createTexture:it_is_invalid_to_have_an_output_attachment_texture_with_non_renderable_format:format="r8snorm"]
expected: FAIL
[webgpu:api,validation,createTexture:it_is_invalid_to_submit_a_destroyed_texture_before_and_after_encode:destroyBeforeEncode=false;destroyAfterEncode=true]
expected: FAIL
[webgpu:api,validation,createTexture:it_is_invalid_to_have_an_output_attachment_texture_with_non_renderable_format:format="rg11b10float"]
expected: FAIL
[webgpu:api,validation,createTexture:it_is_invalid_to_submit_a_destroyed_texture_before_and_after_encode:destroyBeforeEncode=true;destroyAfterEncode=false]
expected: FAIL
[webgpu:api,validation,createTexture:validation_of_sampleCount:sampleCount=2]
expected: FAIL
[webgpu:api,validation,createTexture:it_is_invalid_to_have_an_output_attachment_texture_with_non_renderable_format:format="bc4-r-unorm"]
expected: FAIL
[webgpu:api,validation,createTexture:validation_of_sampleCount:sampleCount=8]
expected: FAIL
[webgpu:api,validation,createTexture:validation_of_sampleCount:sampleCount=16]
expected: FAIL
[webgpu:api,validation,createTexture:validation_of_sampleCount:sampleCount=4;arrayLayerCount=2]
expected: FAIL
[webgpu:api,validation,createTexture:validation_of_mipLevelCount:width=32;height=32;mipLevelCount=0]
expected: FAIL
[webgpu:api,validation,createTexture:it_is_invalid_to_have_an_output_attachment_texture_with_non_renderable_format:format="bc1-rgba-unorm-srgb"]
expected: FAIL
[webgpu:api,validation,createTexture:it_is_invalid_to_have_an_output_attachment_texture_with_non_renderable_format:format="bc6h-rgb-ufloat"]
expected: FAIL
[webgpu:api,validation,createTexture:it_is_invalid_to_have_an_output_attachment_texture_with_non_renderable_format:format="bc7-rgba-unorm-srgb"]
expected: FAIL
[webgpu:api,validation,createTexture:validation_of_mipLevelCount:width=32;height=32;mipLevelCount=100]
expected: FAIL
[webgpu:api,validation,createTexture:it_is_invalid_to_have_an_output_attachment_texture_with_non_renderable_format:format="bc5-rg-unorm"]
expected: FAIL
[webgpu:api,validation,createTexture:it_is_invalid_to_have_an_output_attachment_texture_with_non_renderable_format:format="bc6h-rgb-sfloat"]
expected: FAIL
[webgpu:api,validation,createTexture:it_is_invalid_to_have_an_output_attachment_texture_with_non_renderable_format:format="bc7-rgba-unorm"]
expected: FAIL
[webgpu:api,validation,createTexture:validation_of_sampleCount:sampleCount=4;mipLevelCount=2]
expected: FAIL
[webgpu:api,validation,createTexture:it_is_invalid_to_have_an_output_attachment_texture_with_non_renderable_format:format="rg8snorm"]
expected: FAIL
[webgpu:api,validation,createTexture:it_is_invalid_to_have_an_output_attachment_texture_with_non_renderable_format:format="bc1-rgba-unorm"]
expected: FAIL
[webgpu:api,validation,createTexture:it_is_invalid_to_have_an_output_attachment_texture_with_non_renderable_format:format="bc4-r-snorm"]
expected: FAIL
[webgpu:api,validation,createTexture:it_is_invalid_to_have_an_output_attachment_texture_with_non_renderable_format:format="bc3-rgba-unorm-srgb"]
expected: FAIL
[webgpu:api,validation,createTexture:it_is_invalid_to_have_an_output_attachment_texture_with_non_renderable_format:format="rgba8snorm"]
expected: FAIL
[webgpu:api,validation,createTexture:it_is_invalid_to_have_an_output_attachment_texture_with_non_renderable_format:format="bc5-rg-snorm"]
expected: FAIL
[webgpu:api,validation,createTexture:validation_of_mipLevelCount:width=31;height=32;mipLevelCount=7]
expected: FAIL
[webgpu:api,validation,createTexture:it_is_invalid_to_have_an_output_attachment_texture_with_non_renderable_format:format="bc2-rgba-unorm-srgb"]
expected: FAIL
[cts.html?q=webgpu:api,validation,setViewport:*]
[webgpu:api,validation,setViewport:use_of_setViewport:x=0;y=0;width=1;height=-1;minDepth=0;maxDepth=1]
expected: FAIL
[webgpu:api,validation,setViewport:use_of_setViewport:x=0;y=0;width=1;height=1;minDepth=10;maxDepth=1]
expected: FAIL
[webgpu:api,validation,setViewport:use_of_setViewport:x=0;y=0;width=1;height=0;minDepth=0;maxDepth=1]
expected: FAIL
[webgpu:api,validation,setViewport:use_of_setViewport:x=0;y=0;width=1;height=1;minDepth=-1;maxDepth=1]
expected: FAIL
[webgpu:api,validation,setViewport:use_of_setViewport:x=0;y=0;width=0;height=1;minDepth=0;maxDepth=1]
expected: FAIL
[webgpu:api,validation,setViewport:use_of_setViewport:x=0;y=0;width=1;height=1;minDepth=0;maxDepth=-1]
expected: FAIL
[webgpu:api,validation,setViewport:use_of_setViewport:x=0;y=0;width=0;height=0;minDepth=0;maxDepth=1]
expected: FAIL
[webgpu:api,validation,setViewport:use_of_setViewport:x=0;y=0;width=-1;height=1;minDepth=0;maxDepth=1]
expected: FAIL
[webgpu:api,validation,setViewport:use_of_setViewport:x=0;y=0;width=1;height=1;minDepth=0;maxDepth=10]
expected: FAIL
[cts.html?q=webgpu:web-platform,copyImageBitmapToTexture:*]
expected: TIMEOUT
@ -183,15 +242,6 @@
[cts.html?q=webgpu:api,validation,setScissorRect:*]
[webgpu:api,validation,setScissorRect:use_of_setScissorRect:x=0;y=0;width=0;height=1]
expected: FAIL
[webgpu:api,validation,setScissorRect:use_of_setScissorRect:x=0;y=0;width=0;height=0]
expected: FAIL
[webgpu:api,validation,setScissorRect:use_of_setScissorRect:x=0;y=0;width=1;height=0]
expected: FAIL
[cts.html?q=webgpu:web-platform,canvas,context_creation:*]
@ -222,15 +272,9 @@
[cts.html?q=webgpu:api,operation,command_buffer,render,basic:*]
[cts.html?q=webgpu:api,validation,copyBufferToBuffer:*]
[webgpu:api,validation,copyBufferToBuffer:copy_within_same_buffer:srcOffset=4;dstOffset=0;copySize=8]
expected: FAIL
[webgpu:api,validation,copyBufferToBuffer:buffer_usage:srcUsage=64;dstUsage=512]
expected: FAIL
[webgpu:api,validation,copyBufferToBuffer:copy_out_of_bounds:srcOffset=0;dstOffset=36;copySize=0]
expected: FAIL
[webgpu:api,validation,copyBufferToBuffer:buffer_usage:srcUsage=512;dstUsage=8]
expected: FAIL
@ -243,9 +287,6 @@
[webgpu:api,validation,copyBufferToBuffer:buffer_usage:srcUsage=512;dstUsage=1]
expected: FAIL
[webgpu:api,validation,copyBufferToBuffer:copy_out_of_bounds:srcOffset=36;dstOffset=0;copySize=0]
expected: FAIL
[webgpu:api,validation,copyBufferToBuffer:buffer_usage:srcUsage=8;dstUsage=512]
expected: FAIL
@ -255,15 +296,9 @@
[webgpu:api,validation,copyBufferToBuffer:buffer_usage:srcUsage=1;dstUsage=512]
expected: FAIL
[webgpu:api,validation,copyBufferToBuffer:copy_within_same_buffer:srcOffset=0;dstOffset=4;copySize=8]
expected: FAIL
[webgpu:api,validation,copyBufferToBuffer:buffer_usage:srcUsage=2;dstUsage=512]
expected: FAIL
[webgpu:api,validation,copyBufferToBuffer:copy_within_same_buffer:srcOffset=0;dstOffset=8;copySize=4]
expected: FAIL
[webgpu:api,validation,copyBufferToBuffer:buffer_usage:srcUsage=4;dstUsage=512]
expected: FAIL
@ -279,9 +314,6 @@
[webgpu:api,validation,copyBufferToBuffer:buffer_usage:srcUsage=16;dstUsage=512]
expected: FAIL
[webgpu:api,validation,copyBufferToBuffer:copy_within_same_buffer:srcOffset=8;dstOffset=0;copySize=4]
expected: FAIL
[webgpu:api,validation,copyBufferToBuffer:buffer_usage:srcUsage=512;dstUsage=64]
expected: FAIL
@ -303,3 +335,6 @@
[cts.html?q=webgpu:api,validation,copy_between_linear_data_and_texture,copyBetweenLinearDataAndTexture_dataRelated:*]
expected: CRASH
[cts.html?q=webgpu:api,operation,copyBetweenLinearDataAndTexture:*]
expected: TIMEOUT

View file

@ -3,6 +3,9 @@
**/ import { comparePublicParamsPaths, Ordering } from './query/compare.js';
import { kWildcard, kParamSeparator, kParamKVSeparator } from './query/separators.js';
// Consider adding more types here if needed
//
// TODO: This type isn't actually used to constrain what you're allowed to do in `.params()`, so
// it's not really serving its purpose. Figure out how to fix that?
export function paramKeyIsPublic(key) {
return !key.startsWith('_');

View file

@ -17,6 +17,8 @@
ret = ret.replace(/%3D/g, '='); // for params (k=v)
ret = ret.replace(/%5B/g, '['); // for JSON arrays
ret = ret.replace(/%5D/g, ']'); // for JSON arrays
ret = ret.replace(/%7B/g, '{'); // for JSON objects
ret = ret.replace(/%7D/g, '}'); // for JSON objects
ret = ret.replace(/%E2%9C%97/g, '✗'); // for jsUndefinedMagicValue
return ret;
}

View file

@ -26,9 +26,29 @@ function parseQueryImpl(s) {
// Undo encodeURIComponentSelectively
s = decodeURIComponent(s);
// bigParts are: suite, group, test, params (note kBigSeparator could appear in params)
const [suite, fileString, testString, paramsString] = s.split(kBigSeparator, 4);
assert(fileString !== undefined, `filter string must have at least one ${kBigSeparator}`);
// bigParts are: suite, file, test, params (note kBigSeparator could appear in params)
let suite;
let fileString;
let testString;
let paramsString;
{
const i1 = s.indexOf(kBigSeparator);
assert(i1 !== -1, `query string must have at least one ${kBigSeparator}`);
suite = s.substring(0, i1);
const i2 = s.indexOf(kBigSeparator, i1 + 1);
if (i2 === -1) {
fileString = s.substring(i1 + 1);
} else {
fileString = s.substring(i1 + 1, i2);
const i3 = s.indexOf(kBigSeparator, i2 + 1);
if (i3 === -1) {
testString = s.substring(i2 + 1);
} else {
testString = s.substring(i2 + 1, i3);
paramsString = s.substring(i3 + 1);
}
}
}
const { parts: file, wildcard: filePathHasWildcard } = parseBigPart(fileString, kPathSeparator);

View file

@ -1,3 +1,3 @@
// AUTO-GENERATED - DO NOT EDIT. See tools/gen_version.
export const version = 'fa4873f0a303566ca6f34744a253d96f5e462d3d';
export const version = 'c1df7f4ff1adcde985384633e7cffa52d53e3535';

View file

@ -33,6 +33,7 @@
<meta name=variant content='?q=webgpu:api,operation,command_buffer,basic:*'>
<meta name=variant content='?q=webgpu:api,operation,command_buffer,copies:*'>
<meta name=variant content='?q=webgpu:api,operation,command_buffer,render,basic:*'>
<meta name=variant content='?q=webgpu:api,operation,copyBetweenLinearDataAndTexture:*'>
<meta name=variant content='?q=webgpu:api,operation,fences:*'>
<meta name=variant content='?q=webgpu:api,operation,render_pass,storeOp:*'>
<!--<meta name=variant content='?q=webgpu:api,operation,resource_init,copied_texture_clear:*'>-->
@ -42,7 +43,7 @@
<meta name=variant content='?q=webgpu:api,validation,createBindGroup:*'>
<!--<meta name=variant content='?q=webgpu:api,validation,createBindGroupLayout:*'>-->
<meta name=variant content='?q=webgpu:api,validation,createPipelineLayout:*'>
<meta name=variant content='?q=webgpu:api,validation,createTexture:*'>
<!--<meta name=variant content='?q=webgpu:api,validation,createTexture:*'>-->
<!--<meta name=variant content='?q=webgpu:api,validation,createView:*'>-->
<meta name=variant content='?q=webgpu:api,validation,error_scope:*'>
<meta name=variant content='?q=webgpu:api,validation,fences:*'>

View file

@ -21,13 +21,22 @@ Test Coverage:
- Test combinations of two shader stages:
- Texture usages in bindings with invisible shader stages should be tracked. Invisible shader
stages include shader stage with visibility none and compute shader stage in render pass.
- Tests replaced bindings:
- Texture usages via bindings replaced by another setBindGroup() upon the same bindGroup index
in current scope should be tracked.
- Test texture usages in bundle:
- Texture usages in bundle should be tracked if that bundle is executed in the current scope.
`;
import { poptions, params } from '../../../../common/framework/params_builder.js';
import { pbool, poptions, params } from '../../../../common/framework/params_builder.js';
import { makeTestGroup } from '../../../../common/framework/test_group.js';
import {
kShaderStages,
kDepthStencilFormats,
kDepthStencilFormatInfo,
kTextureBindingTypes,
kTextureBindingTypeInfo,
kShaderStages,
} from '../../../capability_info.js';
import { ValidationTest } from '../validation_test.js';
@ -52,6 +61,22 @@ class TextureUsageTracking extends ValidationTest {
usage,
});
}
createBindGroup(index, view, bindingType, bindingTexFormat) {
return this.device.createBindGroup({
entries: [{ binding: index, resource: view }],
layout: this.device.createBindGroupLayout({
entries: [
{
binding: index,
visibility: GPUShaderStage.FRAGMENT,
type: bindingType,
storageTextureFormat: bindingTexFormat,
},
],
}),
});
}
}
export const g = makeTestGroup(TextureUsageTracking);
@ -400,3 +425,158 @@ g.test('shader_stages_and_visibility')
encoder.finish();
});
});
// We should track the texture usages in bindings which are replaced by another setBindGroup()
// call site upon the same index in the same render pass.
g.test('replaced_binding')
.params(poptions('bindingType', kTextureBindingTypes))
.fn(async t => {
const { bindingType } = t.params;
const info = kTextureBindingTypeInfo[bindingType];
const bindingTexFormat = info.resource === 'storageTex' ? 'rgba8unorm' : undefined;
const sampledView = t.createTexture().createView();
const sampledStorageView = t
.createTexture({ usage: GPUTextureUsage.STORAGE | GPUTextureUsage.SAMPLED })
.createView();
// Create bindGroup0. It has two bindings. These two bindings use different views/subresources.
const bglEntries0 = [
{ binding: 0, visibility: GPUShaderStage.FRAGMENT, type: 'sampled-texture' },
{
binding: 1,
visibility: GPUShaderStage.FRAGMENT,
type: bindingType,
storageTextureFormat: bindingTexFormat,
},
];
const bgEntries0 = [
{ binding: 0, resource: sampledView },
{ binding: 1, resource: sampledStorageView },
];
const bindGroup0 = t.device.createBindGroup({
entries: bgEntries0,
layout: t.device.createBindGroupLayout({ entries: bglEntries0 }),
});
// Create bindGroup1. It has one binding, which use the same view/subresoure of a binding in
// bindGroup0. So it may or may not conflicts with that binding in bindGroup0.
const bindGroup1 = t.createBindGroup(0, sampledStorageView, 'sampled-texture', undefined);
const encoder = t.device.createCommandEncoder();
const pass = encoder.beginRenderPass({
colorAttachments: [
{
attachment: t.createTexture().createView(),
loadValue: { r: 0.0, g: 1.0, b: 0.0, a: 1.0 },
storeOp: 'store',
},
],
});
// Set bindGroup0 and bindGroup1. bindGroup0 is replaced by bindGroup1 in the current pass.
// But bindings in bindGroup0 should be tracked too.
pass.setBindGroup(0, bindGroup0);
pass.setBindGroup(0, bindGroup1);
pass.endPass();
const success = bindingType === 'writeonly-storage-texture' ? false : true;
t.expectValidationError(() => {
encoder.finish();
}, !success);
});
g.test('bindings_in_bundle')
.params(
params()
.combine(pbool('binding0InBundle'))
.combine(pbool('binding1InBundle'))
.combine(poptions('type0', ['render-target', ...kTextureBindingTypes]))
.combine(poptions('type1', ['render-target', ...kTextureBindingTypes]))
.unless(
({ binding0InBundle, binding1InBundle, type0, type1 }) =>
// We can't set 'render-target' in bundle, so we need to exclude it from bundle.
// In addition, if both bindings are non-bundle, there is no need to test it because
// we have far more comprehensive test cases for that situation in this file.
(binding0InBundle && type0 === 'render-target') ||
(binding1InBundle && type1 === 'render-target') ||
(!binding0InBundle && !binding1InBundle)
)
)
.fn(async t => {
const { binding0InBundle, binding1InBundle, type0, type1 } = t.params;
// Two bindings are attached to the same texture view.
const view = t
.createTexture({
usage:
GPUTextureUsage.OUTPUT_ATTACHMENT | GPUTextureUsage.STORAGE | GPUTextureUsage.SAMPLED,
})
.createView();
const bindGroups = [];
if (type0 !== 'render-target') {
const binding0TexFormat = type0 === 'sampled-texture' ? undefined : 'rgba8unorm';
bindGroups[0] = t.createBindGroup(0, view, type0, binding0TexFormat);
}
if (type1 !== 'render-target') {
const binding1TexFormat = type1 === 'sampled-texture' ? undefined : 'rgba8unorm';
bindGroups[1] = t.createBindGroup(1, view, type1, binding1TexFormat);
}
const encoder = t.device.createCommandEncoder();
const pass = encoder.beginRenderPass({
colorAttachments: [
{
attachment:
// At least one binding is in bundle, which means that its type is not 'render-target'.
// As a result, only one binding's type is 'render-target' at most.
type0 === 'render-target' || type1 === 'render-target'
? view
: t.createTexture().createView(),
loadValue: { r: 0.0, g: 1.0, b: 0.0, a: 1.0 },
storeOp: 'store',
},
],
});
const bindingsInBundle = [binding0InBundle, binding1InBundle];
for (let i = 0; i < 2; i++) {
// Create a bundle for each bind group if its bindings is required to be in bundle on purpose.
// Otherwise, call setBindGroup directly in pass if needed (when its binding is not
// 'render-target').
if (bindingsInBundle[i]) {
const bundleEncoder = t.device.createRenderBundleEncoder({
colorFormats: ['rgba8unorm'],
});
bundleEncoder.setBindGroup(i, bindGroups[i]);
const bundleInPass = bundleEncoder.finish();
pass.executeBundles([bundleInPass]);
} else if (bindGroups[i] !== undefined) {
pass.setBindGroup(i, bindGroups[i]);
}
}
pass.endPass();
let success = false;
if (
(type0 === 'sampled-texture' || type0 === 'readonly-storage-texture') &&
(type1 === 'sampled-texture' || type1 === 'readonly-storage-texture')
) {
success = true;
}
if (type0 === 'writeonly-storage-texture' && type1 === 'writeonly-storage-texture') {
success = true;
}
// Resource usages in bundle should be tracked. And validation error should be reported
// if needed.
t.expectValidationError(() => {
encoder.finish();
}, !success);
});

View file

@ -38,7 +38,7 @@ g.test('use_of_setScissorRect')
{ x: 0, y: 0, width: 0, height: 1, _success: false }, // Width of zero is not allowed
{ x: 0, y: 0, width: 1, height: 0, _success: false }, // Height of zero is not allowed
{ x: 0, y: 0, width: 0, height: 0, _success: false }, // Both width and height of zero are not allowed
{ x: 0, y: 0, width: TEXTURE_WIDTH + 1, height: TEXTURE_HEIGHT + 1, _success: true }, // Scissor larger than the framebuffer is allowed
{ x: 0, y: 0, width: TEXTURE_WIDTH + 1, height: TEXTURE_HEIGHT + 1, _success: false }, // Scissor larger than the framebuffer is not allowed
])
.fn(async t => {
const { x, y, width, height, _success } = t.params;

View file

@ -18,6 +18,7 @@ import { DevicePool, TestOOMedShouldAttemptGC } from '../common/framework/gpu/de
import { attemptGarbageCollection } from '../common/framework/util/collect_garbage.js';
import { assert } from '../common/framework/util/util.js';
import { align } from './util/math.js';
import { fillTextureDataWithTexelValue, getTextureCopyLayout } from './util/texture/layout.js';
import { getTexelDataRepresentation } from './util/texture/texelData.js';
@ -76,6 +77,9 @@ export class GPUTest extends Fixture {
}
createCopyForMapRead(src, srcOffset, size) {
assert(srcOffset % 4 === 0);
assert(size % 4 === 0);
const dst = this.device.createBuffer({
size,
usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST,
@ -91,14 +95,31 @@ export class GPUTest extends Fixture {
// TODO: add an expectContents for textures, which logs data: uris on failure
// Offset and size passed to createCopyForMapRead must be divisible by 4. For that
// we might need to copy more bytes from the buffer than we want to map.
// begin and end values represent the part of the copied buffer that stores the contents
// we initially wanted to map.
// The copy will not cause an OOB error because the buffer size must be 4-aligned.
createAlignedCopyForMapRead(src, size, offset) {
const alignedOffset = Math.floor(offset / 4) * 4;
const offsetDifference = offset - alignedOffset;
const alignedSize = align(size + offsetDifference, 4);
const dst = this.createCopyForMapRead(src, alignedOffset, alignedSize);
return { dst, begin: offsetDifference, end: offsetDifference + size };
}
expectContents(src, expected, srcOffset = 0) {
const dst = this.createCopyForMapRead(src, srcOffset, expected.buffer.byteLength);
const { dst, begin, end } = this.createAlignedCopyForMapRead(
src,
expected.byteLength,
srcOffset
);
this.eventualAsyncExpectation(async niceStack => {
const constructor = expected.constructor;
await dst.mapAsync(GPUMapMode.READ);
const actual = new constructor(dst.getMappedRange());
const check = this.checkBuffer(actual, expected);
const check = this.checkBuffer(actual.subarray(begin, end), expected);
if (check !== undefined) {
niceStack.message = check;
this.rec.expectationFailed(niceStack);

View file

@ -81,6 +81,14 @@ export const listing = [
],
"description": "Basic command buffer rendering tests."
},
{
"file": [
"api",
"operation",
"copyBetweenLinearDataAndTexture"
],
"description": "writeTexture + copyBufferToTexture + copyTextureToBuffer operation tests.\n\n* copy_with_various_rows_per_image_and_bytes_per_row: test that copying data with various bytesPerRow (including { ==, > } bytesInACompleteRow) and rowsPerImage (including { ==, > } copyExtent.height) values and minimum required bytes in copy works for every format. Also covers special code paths:\n - bufferSize - offset < bytesPerImage * copyExtent.depth\n - when bytesPerRow is not a multiple of 512 and copyExtent.depth > 1: copyExtent.depth % 2 == { 0, 1 }\n - bytesPerRow == bytesInACompleteCopyImage\n\n* copy_with_various_offsets_and_data_sizes: test that copying data with various offset (including { ==, > } 0 and is/isn't power of 2) values and additional data paddings works for every format with 2d and 2d-array textures. Also covers special code paths:\n - offset + bytesInCopyExtentPerRow { ==, > } bytesPerRow\n - offset > bytesInACompleteCopyImage\n\n* copy_with_various_origins_and_copy_extents: test that copying slices of a texture works with various origin (including { origin.x, origin.y, origin.z } { ==, > } 0 and is/isn't power of 2) and copyExtent (including { copyExtent.x, copyExtent.y, copyExtent.z } { ==, > } 0 and is/isn't power of 2) values (also including {origin._ + copyExtent._ { ==, < } the subresource size of textureCopyView) works for all formats. origin and copyExtent values are passed as [number, number, number] instead of GPUExtent3DDict.\n\n* copy_various_mip_levels: test that copying various mip levels works for all formats. Also covers special code paths:\n - the physical size of the subresouce is not equal to the logical size\n - bufferSize - offset < bytesPerImage * copyExtent.depth and copyExtent needs to be clamped\n\n* copy_with_no_image_or_slice_padding_and_undefined_values: test that when copying a single row we can set any bytesPerRow value and when copying a single slice we can set rowsPerImage to 0. Also test setting offset, rowsPerImage, mipLevel, origin, origin.{x,y,z} to undefined.\n\n* TODO:\n - add another initMethod which renders the texture\n - because of expectContests 4-bytes alignment we don't test CopyT2B with buffer size not divisible by 4\n - add tests for 1d / 3d textures"
},
{
"file": [
"api",
@ -252,7 +260,7 @@ export const listing = [
"resource_usages",
"textureUsageInRender"
],
"description": "Texture Usages Validation Tests in Render Pass.\n\nTest Coverage:\n\n - For each combination of two texture usages:\n - For various subresource ranges (different mip levels or array layers) that overlap a given\n subresources or not for color formats:\n - Check that an error is generated when read-write or write-write usages are binding to the\n same texture subresource. Otherwise, no error should be generated. One exception is race\n condition upon two writeonly-storage-texture usages, which is valid.\n\n - For each combination of two texture usages:\n - For various aspects (all, depth-only, stencil-only) that overlap a given subresources or not\n for depth/stencil formats:\n - Check that an error is generated when read-write or write-write usages are binding to the\n same aspect. Otherwise, no error should be generated.\n\n - Test combinations of two shader stages:\n - Texture usages in bindings with invisible shader stages should be tracked. Invisible shader\n stages include shader stage with visibility none and compute shader stage in render pass."
"description": "Texture Usages Validation Tests in Render Pass.\n\nTest Coverage:\n\n - For each combination of two texture usages:\n - For various subresource ranges (different mip levels or array layers) that overlap a given\n subresources or not for color formats:\n - Check that an error is generated when read-write or write-write usages are binding to the\n same texture subresource. Otherwise, no error should be generated. One exception is race\n condition upon two writeonly-storage-texture usages, which is valid.\n\n - For each combination of two texture usages:\n - For various aspects (all, depth-only, stencil-only) that overlap a given subresources or not\n for depth/stencil formats:\n - Check that an error is generated when read-write or write-write usages are binding to the\n same aspect. Otherwise, no error should be generated.\n\n - Test combinations of two shader stages:\n - Texture usages in bindings with invisible shader stages should be tracked. Invisible shader\n stages include shader stage with visibility none and compute shader stage in render pass.\n\n - Tests replaced bindings:\n - Texture usages via bindings replaced by another setBindGroup() upon the same bindGroup index\n in current scope should be tracked.\n\n - Test texture usages in bundle:\n - Texture usages in bundle should be tracked if that bundle is executed in the current scope."
},
{
"file": [

View file

@ -32,7 +32,10 @@ export function getTextureCopyLayout(format, dimension, size, options = kDefault
const { blockWidth, blockHeight, bytesPerBlock } = kSizedTextureFormatInfo[format];
assert(isAligned(mipSize[0], blockWidth));
// We align mipSize to be the physical size of the texture subresource.
mipSize[0] = align(mipSize[0], blockWidth);
mipSize[1] = align(mipSize[1], blockHeight);
const minBytesPerRow = (mipSize[0] / blockWidth) * bytesPerBlock;
const alignedMinBytesPerRow = align(minBytesPerRow, kBytesPerRowAlignment);
if (bytesPerRow !== undefined) {