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

View file

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

View file

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

View file

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

View file

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

View file

@ -30,36 +30,95 @@
[cts.html?q=webgpu:api,validation,createTexture:*] [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:*] [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:*] [cts.html?q=webgpu:web-platform,copyImageBitmapToTexture:*]
expected: TIMEOUT expected: TIMEOUT
@ -183,15 +242,6 @@
[cts.html?q=webgpu:api,validation,setScissorRect:*] [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:*] [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,operation,command_buffer,render,basic:*]
[cts.html?q=webgpu:api,validation,copyBufferToBuffer:*] [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] [webgpu:api,validation,copyBufferToBuffer:buffer_usage:srcUsage=64;dstUsage=512]
expected: FAIL 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] [webgpu:api,validation,copyBufferToBuffer:buffer_usage:srcUsage=512;dstUsage=8]
expected: FAIL expected: FAIL
@ -243,9 +287,6 @@
[webgpu:api,validation,copyBufferToBuffer:buffer_usage:srcUsage=512;dstUsage=1] [webgpu:api,validation,copyBufferToBuffer:buffer_usage:srcUsage=512;dstUsage=1]
expected: FAIL 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] [webgpu:api,validation,copyBufferToBuffer:buffer_usage:srcUsage=8;dstUsage=512]
expected: FAIL expected: FAIL
@ -255,15 +296,9 @@
[webgpu:api,validation,copyBufferToBuffer:buffer_usage:srcUsage=1;dstUsage=512] [webgpu:api,validation,copyBufferToBuffer:buffer_usage:srcUsage=1;dstUsage=512]
expected: FAIL 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] [webgpu:api,validation,copyBufferToBuffer:buffer_usage:srcUsage=2;dstUsage=512]
expected: FAIL 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] [webgpu:api,validation,copyBufferToBuffer:buffer_usage:srcUsage=4;dstUsage=512]
expected: FAIL expected: FAIL
@ -279,9 +314,6 @@
[webgpu:api,validation,copyBufferToBuffer:buffer_usage:srcUsage=16;dstUsage=512] [webgpu:api,validation,copyBufferToBuffer:buffer_usage:srcUsage=16;dstUsage=512]
expected: FAIL 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] [webgpu:api,validation,copyBufferToBuffer:buffer_usage:srcUsage=512;dstUsage=64]
expected: FAIL expected: FAIL
@ -303,3 +335,6 @@
[cts.html?q=webgpu:api,validation,copy_between_linear_data_and_texture,copyBetweenLinearDataAndTexture_dataRelated:*] [cts.html?q=webgpu:api,validation,copy_between_linear_data_and_texture,copyBetweenLinearDataAndTexture_dataRelated:*]
expected: CRASH 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 { comparePublicParamsPaths, Ordering } from './query/compare.js';
import { kWildcard, kParamSeparator, kParamKVSeparator } from './query/separators.js'; import { kWildcard, kParamSeparator, kParamKVSeparator } from './query/separators.js';
// Consider adding more types here if needed // 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) { export function paramKeyIsPublic(key) {
return !key.startsWith('_'); return !key.startsWith('_');

View file

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

View file

@ -26,9 +26,29 @@ function parseQueryImpl(s) {
// Undo encodeURIComponentSelectively // Undo encodeURIComponentSelectively
s = decodeURIComponent(s); s = decodeURIComponent(s);
// bigParts are: suite, group, test, params (note kBigSeparator could appear in params) // bigParts are: suite, file, test, params (note kBigSeparator could appear in params)
const [suite, fileString, testString, paramsString] = s.split(kBigSeparator, 4); let suite;
assert(fileString !== undefined, `filter string must have at least one ${kBigSeparator}`); 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); const { parts: file, wildcard: filePathHasWildcard } = parseBigPart(fileString, kPathSeparator);

View file

@ -1,3 +1,3 @@
// AUTO-GENERATED - DO NOT EDIT. See tools/gen_version. // 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,basic:*'>
<meta name=variant content='?q=webgpu:api,operation,command_buffer,copies:*'> <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,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,fences:*'>
<meta name=variant content='?q=webgpu:api,operation,render_pass,storeOp:*'> <meta name=variant content='?q=webgpu:api,operation,render_pass,storeOp:*'>
<!--<meta name=variant content='?q=webgpu:api,operation,resource_init,copied_texture_clear:*'>--> <!--<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,createBindGroup:*'>
<!--<meta name=variant content='?q=webgpu:api,validation,createBindGroupLayout:*'>--> <!--<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,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,createView:*'>-->
<meta name=variant content='?q=webgpu:api,validation,error_scope:*'> <meta name=variant content='?q=webgpu:api,validation,error_scope:*'>
<meta name=variant content='?q=webgpu:api,validation,fences:*'> <meta name=variant content='?q=webgpu:api,validation,fences:*'>

View file

@ -21,13 +21,22 @@ Test Coverage:
- Test combinations of two shader stages: - Test combinations of two shader stages:
- Texture usages in bindings with invisible shader stages should be tracked. Invisible shader - 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. 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 { makeTestGroup } from '../../../../common/framework/test_group.js';
import { import {
kShaderStages,
kDepthStencilFormats, kDepthStencilFormats,
kDepthStencilFormatInfo, kDepthStencilFormatInfo,
kTextureBindingTypes,
kTextureBindingTypeInfo,
kShaderStages,
} from '../../../capability_info.js'; } from '../../../capability_info.js';
import { ValidationTest } from '../validation_test.js'; import { ValidationTest } from '../validation_test.js';
@ -52,6 +61,22 @@ class TextureUsageTracking extends ValidationTest {
usage, 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); export const g = makeTestGroup(TextureUsageTracking);
@ -400,3 +425,158 @@ g.test('shader_stages_and_visibility')
encoder.finish(); 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: 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: 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: 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 => { .fn(async t => {
const { x, y, width, height, _success } = t.params; 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 { attemptGarbageCollection } from '../common/framework/util/collect_garbage.js';
import { assert } from '../common/framework/util/util.js'; import { assert } from '../common/framework/util/util.js';
import { align } from './util/math.js';
import { fillTextureDataWithTexelValue, getTextureCopyLayout } from './util/texture/layout.js'; import { fillTextureDataWithTexelValue, getTextureCopyLayout } from './util/texture/layout.js';
import { getTexelDataRepresentation } from './util/texture/texelData.js'; import { getTexelDataRepresentation } from './util/texture/texelData.js';
@ -76,6 +77,9 @@ export class GPUTest extends Fixture {
} }
createCopyForMapRead(src, srcOffset, size) { createCopyForMapRead(src, srcOffset, size) {
assert(srcOffset % 4 === 0);
assert(size % 4 === 0);
const dst = this.device.createBuffer({ const dst = this.device.createBuffer({
size, size,
usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST, 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 // 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) { 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 => { this.eventualAsyncExpectation(async niceStack => {
const constructor = expected.constructor; const constructor = expected.constructor;
await dst.mapAsync(GPUMapMode.READ); await dst.mapAsync(GPUMapMode.READ);
const actual = new constructor(dst.getMappedRange()); const actual = new constructor(dst.getMappedRange());
const check = this.checkBuffer(actual, expected); const check = this.checkBuffer(actual.subarray(begin, end), expected);
if (check !== undefined) { if (check !== undefined) {
niceStack.message = check; niceStack.message = check;
this.rec.expectationFailed(niceStack); this.rec.expectationFailed(niceStack);

View file

@ -81,6 +81,14 @@ export const listing = [
], ],
"description": "Basic command buffer rendering tests." "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": [ "file": [
"api", "api",
@ -252,7 +260,7 @@ export const listing = [
"resource_usages", "resource_usages",
"textureUsageInRender" "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": [ "file": [

View file

@ -32,7 +32,10 @@ export function getTextureCopyLayout(format, dimension, size, options = kDefault
const { blockWidth, blockHeight, bytesPerBlock } = kSizedTextureFormatInfo[format]; 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 minBytesPerRow = (mipSize[0] / blockWidth) * bytesPerBlock;
const alignedMinBytesPerRow = align(minBytesPerRow, kBytesPerRowAlignment); const alignedMinBytesPerRow = align(minBytesPerRow, kBytesPerRowAlignment);
if (bytesPerRow !== undefined) { if (bytesPerRow !== undefined) {