mirror of
https://github.com/gfx-rs/wgpu.git
synced 2026-04-22 03:02:01 -04:00
320 lines
12 KiB
Rust
320 lines
12 KiB
Rust
use wgpu::*;
|
|
use wgpu_test::{
|
|
gpu_test, image::ReadbackBuffers, FailureCase, GpuTestConfiguration, TestParameters,
|
|
TestingContext,
|
|
};
|
|
|
|
// Checks if discarding a color target resets its init state, causing a zero read of this texture when copied in after submit of the encoder.
|
|
#[gpu_test]
|
|
static DISCARDING_COLOR_TARGET_RESETS_TEXTURE_INIT_STATE_CHECK_VISIBLE_ON_COPY_AFTER_SUBMIT:
|
|
GpuTestConfiguration = GpuTestConfiguration::new()
|
|
.parameters(TestParameters::default().expect_fail(FailureCase::webgl2()))
|
|
.run_async(|mut ctx| async move {
|
|
let mut case = TestCase::new(&mut ctx, TextureFormat::Rgba8UnormSrgb);
|
|
case.create_command_encoder();
|
|
case.discard();
|
|
case.submit_command_encoder();
|
|
|
|
case.create_command_encoder();
|
|
case.copy_texture_to_buffer();
|
|
case.submit_command_encoder();
|
|
|
|
case.assert_buffers_are_zero().await;
|
|
});
|
|
|
|
#[gpu_test]
|
|
static DISCARDING_COLOR_TARGET_RESETS_TEXTURE_INIT_STATE_CHECK_VISIBLE_ON_COPY_IN_SAME_ENCODER:
|
|
GpuTestConfiguration = GpuTestConfiguration::new()
|
|
.parameters(TestParameters::default().expect_fail(FailureCase::webgl2()))
|
|
.run_async(|mut ctx| async move {
|
|
let mut case = TestCase::new(&mut ctx, TextureFormat::Rgba8UnormSrgb);
|
|
case.create_command_encoder();
|
|
case.discard();
|
|
case.copy_texture_to_buffer();
|
|
case.submit_command_encoder();
|
|
|
|
case.assert_buffers_are_zero().await;
|
|
});
|
|
|
|
#[gpu_test]
|
|
static DISCARDING_DEPTH_TARGET_RESETS_TEXTURE_INIT_STATE_CHECK_VISIBLE_ON_COPY_IN_SAME_ENCODER:
|
|
GpuTestConfiguration = GpuTestConfiguration::new()
|
|
.parameters(
|
|
TestParameters::default()
|
|
.downlevel_flags(
|
|
DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES | DownlevelFlags::COMPUTE_SHADERS,
|
|
)
|
|
.limits(Limits::downlevel_defaults()),
|
|
)
|
|
.run_async(|mut ctx| async move {
|
|
for format in [
|
|
TextureFormat::Stencil8,
|
|
TextureFormat::Depth16Unorm,
|
|
TextureFormat::Depth24Plus,
|
|
TextureFormat::Depth24PlusStencil8,
|
|
TextureFormat::Depth32Float,
|
|
] {
|
|
let mut case = TestCase::new(&mut ctx, format);
|
|
case.create_command_encoder();
|
|
case.discard();
|
|
case.copy_texture_to_buffer();
|
|
case.submit_command_encoder();
|
|
|
|
case.assert_buffers_are_zero().await;
|
|
}
|
|
});
|
|
|
|
#[gpu_test]
|
|
static DISCARDING_EITHER_DEPTH_OR_STENCIL_ASPECT_TEST: GpuTestConfiguration =
|
|
GpuTestConfiguration::new()
|
|
.parameters(
|
|
TestParameters::default()
|
|
.downlevel_flags(
|
|
DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES
|
|
| DownlevelFlags::COMPUTE_SHADERS,
|
|
)
|
|
.limits(Limits::downlevel_defaults()),
|
|
)
|
|
.run_async(|mut ctx| async move {
|
|
for format in [
|
|
TextureFormat::Stencil8,
|
|
TextureFormat::Depth16Unorm,
|
|
TextureFormat::Depth24Plus,
|
|
TextureFormat::Depth24PlusStencil8,
|
|
TextureFormat::Depth32Float,
|
|
] {
|
|
let mut case = TestCase::new(&mut ctx, format);
|
|
case.create_command_encoder();
|
|
case.discard_depth();
|
|
case.submit_command_encoder();
|
|
|
|
case.create_command_encoder();
|
|
case.discard_stencil();
|
|
case.submit_command_encoder();
|
|
|
|
case.create_command_encoder();
|
|
case.copy_texture_to_buffer();
|
|
case.submit_command_encoder();
|
|
|
|
case.assert_buffers_are_zero().await;
|
|
}
|
|
});
|
|
|
|
struct TestCase<'ctx> {
|
|
ctx: &'ctx mut TestingContext,
|
|
format: TextureFormat,
|
|
texture: Texture,
|
|
readback_buffers: ReadbackBuffers,
|
|
encoder: Option<CommandEncoder>,
|
|
}
|
|
|
|
impl<'ctx> TestCase<'ctx> {
|
|
pub fn new(ctx: &'ctx mut TestingContext, format: TextureFormat) -> Self {
|
|
let extra_usages = match format {
|
|
TextureFormat::Depth24Plus | TextureFormat::Depth24PlusStencil8 => {
|
|
TextureUsages::TEXTURE_BINDING
|
|
}
|
|
_ => TextureUsages::empty(),
|
|
};
|
|
|
|
let texture = ctx.device.create_texture(&TextureDescriptor {
|
|
label: Some("RenderTarget"),
|
|
size: Extent3d {
|
|
width: COPY_BYTES_PER_ROW_ALIGNMENT,
|
|
height: COPY_BYTES_PER_ROW_ALIGNMENT,
|
|
depth_or_array_layers: 1,
|
|
},
|
|
mip_level_count: 1,
|
|
sample_count: 1,
|
|
dimension: TextureDimension::D2,
|
|
format,
|
|
usage: TextureUsages::COPY_DST
|
|
| TextureUsages::COPY_SRC
|
|
| TextureUsages::RENDER_ATTACHMENT
|
|
| extra_usages,
|
|
view_formats: &[],
|
|
});
|
|
|
|
// Clear using a write_texture operation. We could also clear using a render_pass clear.
|
|
// However, when making this test intentionally fail (by breaking wgpu impl), it shows that at least on the tested Vulkan driver,
|
|
// the later following discard pass in the test (i.e. internally vk::AttachmentStoreOp::DONT_CARE) will yield different depending on the operation we take here:
|
|
// * clearing white -> discard will cause it to become black!
|
|
// * clearing red -> discard will keep it red
|
|
// * write_texture -> discard will keep buffer
|
|
// This behavior is curious, but does not violate any spec - it is wgpu's job to pass this test no matter what a render target discard does.
|
|
|
|
// ... but that said, for depth/stencil textures we need to do a clear.
|
|
if format.is_depth_stencil_format() {
|
|
let mut encoder = ctx
|
|
.device
|
|
.create_command_encoder(&CommandEncoderDescriptor::default());
|
|
encoder.begin_render_pass(&RenderPassDescriptor {
|
|
label: Some("Depth/Stencil setup"),
|
|
color_attachments: &[],
|
|
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
|
|
view: &texture.create_view(&TextureViewDescriptor::default()),
|
|
depth_ops: format.has_depth_aspect().then_some(Operations {
|
|
load: LoadOp::Clear(1.0),
|
|
store: StoreOp::Store,
|
|
}),
|
|
stencil_ops: format.has_stencil_aspect().then_some(Operations {
|
|
load: LoadOp::Clear(0xFFFFFFFF),
|
|
store: StoreOp::Store,
|
|
}),
|
|
}),
|
|
timestamp_writes: None,
|
|
occlusion_query_set: None,
|
|
});
|
|
ctx.queue.submit([encoder.finish()]);
|
|
} else {
|
|
let block_size = format.block_copy_size(None).unwrap();
|
|
let bytes_per_row = texture.width() * block_size;
|
|
|
|
// Size for tests is chosen so that we don't need to care about buffer alignments.
|
|
assert!(!format.is_compressed());
|
|
assert_eq!(bytes_per_row % COPY_BYTES_PER_ROW_ALIGNMENT, 0);
|
|
|
|
let buffer_size = texture.height() * bytes_per_row;
|
|
let data = vec![255; buffer_size as usize];
|
|
ctx.queue.write_texture(
|
|
TexelCopyTextureInfo {
|
|
texture: &texture,
|
|
mip_level: 0,
|
|
origin: Origin3d { x: 0, y: 0, z: 0 },
|
|
aspect: TextureAspect::All,
|
|
},
|
|
&data,
|
|
TexelCopyBufferLayout {
|
|
offset: 0,
|
|
bytes_per_row: Some(bytes_per_row),
|
|
rows_per_image: None,
|
|
},
|
|
texture.size(),
|
|
);
|
|
}
|
|
|
|
let readback_buffers = ReadbackBuffers::new(&ctx.device, &texture);
|
|
|
|
Self {
|
|
ctx,
|
|
format,
|
|
texture,
|
|
readback_buffers,
|
|
encoder: None,
|
|
}
|
|
}
|
|
|
|
pub fn create_command_encoder(&mut self) {
|
|
self.encoder = Some(
|
|
self.ctx
|
|
.device
|
|
.create_command_encoder(&CommandEncoderDescriptor::default()),
|
|
)
|
|
}
|
|
|
|
pub fn submit_command_encoder(&mut self) {
|
|
self.ctx
|
|
.queue
|
|
.submit([self.encoder.take().unwrap().finish()]);
|
|
}
|
|
|
|
pub fn discard(&mut self) {
|
|
self.encoder
|
|
.as_mut()
|
|
.unwrap()
|
|
.begin_render_pass(&RenderPassDescriptor {
|
|
label: Some("Discard"),
|
|
color_attachments: &[self.format.has_color_aspect().then_some(
|
|
RenderPassColorAttachment {
|
|
view: &self.texture.create_view(&TextureViewDescriptor::default()),
|
|
resolve_target: None,
|
|
ops: Operations {
|
|
load: LoadOp::Load,
|
|
store: StoreOp::Discard,
|
|
},
|
|
},
|
|
)],
|
|
depth_stencil_attachment: self.format.is_depth_stencil_format().then_some(
|
|
RenderPassDepthStencilAttachment {
|
|
view: &self.texture.create_view(&TextureViewDescriptor::default()),
|
|
depth_ops: self.format.has_depth_aspect().then_some(Operations {
|
|
load: LoadOp::Load,
|
|
store: StoreOp::Discard,
|
|
}),
|
|
stencil_ops: self.format.has_stencil_aspect().then_some(Operations {
|
|
load: LoadOp::Load,
|
|
store: StoreOp::Discard,
|
|
}),
|
|
},
|
|
),
|
|
timestamp_writes: None,
|
|
occlusion_query_set: None,
|
|
});
|
|
}
|
|
|
|
pub fn discard_depth(&mut self) {
|
|
self.encoder
|
|
.as_mut()
|
|
.unwrap()
|
|
.begin_render_pass(&RenderPassDescriptor {
|
|
label: Some("Discard Depth"),
|
|
color_attachments: &[],
|
|
depth_stencil_attachment: self.format.is_depth_stencil_format().then_some(
|
|
RenderPassDepthStencilAttachment {
|
|
view: &self.texture.create_view(&TextureViewDescriptor::default()),
|
|
depth_ops: Some(Operations {
|
|
load: LoadOp::Load,
|
|
store: StoreOp::Discard,
|
|
}),
|
|
stencil_ops: self.format.has_stencil_aspect().then_some(Operations {
|
|
load: LoadOp::Clear(0),
|
|
store: StoreOp::Store,
|
|
}),
|
|
},
|
|
),
|
|
timestamp_writes: None,
|
|
occlusion_query_set: None,
|
|
});
|
|
}
|
|
|
|
pub fn discard_stencil(&mut self) {
|
|
self.encoder
|
|
.as_mut()
|
|
.unwrap()
|
|
.begin_render_pass(&RenderPassDescriptor {
|
|
label: Some("Discard Stencil"),
|
|
color_attachments: &[],
|
|
depth_stencil_attachment: self.format.is_depth_stencil_format().then_some(
|
|
RenderPassDepthStencilAttachment {
|
|
view: &self.texture.create_view(&TextureViewDescriptor::default()),
|
|
depth_ops: self.format.has_depth_aspect().then_some(Operations {
|
|
load: LoadOp::Clear(0.0),
|
|
store: StoreOp::Store,
|
|
}),
|
|
stencil_ops: Some(Operations {
|
|
load: LoadOp::Load,
|
|
store: StoreOp::Discard,
|
|
}),
|
|
},
|
|
),
|
|
timestamp_writes: None,
|
|
occlusion_query_set: None,
|
|
});
|
|
}
|
|
|
|
pub fn copy_texture_to_buffer(&mut self) {
|
|
self.readback_buffers.copy_from(
|
|
&self.ctx.device,
|
|
self.encoder.as_mut().unwrap(),
|
|
&self.texture,
|
|
);
|
|
}
|
|
|
|
pub async fn assert_buffers_are_zero(&mut self) {
|
|
assert!(
|
|
self.readback_buffers.are_zero(self.ctx).await,
|
|
"texture was not fully cleared"
|
|
);
|
|
}
|
|
}
|