mirror of
https://github.com/gfx-rs/wgpu.git
synced 2025-12-08 21:26:17 +00:00
[wgpu] Add external texture validation tests
Adds validation tests using the noop backend covering creation of external textures, and creation of bind groups containing external textures.
This commit is contained in:
parent
7087f0c01f
commit
f8756a6e1b
@ -1,6 +1,365 @@
|
||||
use wgpu::*;
|
||||
use wgpu_test::{fail, valid};
|
||||
|
||||
/// Ensures an [`ExternalTexture`] can be created from a valid descriptor and planes,
|
||||
/// but appropriate errors are returned for invalid descriptors and planes.
|
||||
#[test]
|
||||
fn create_external_texture() {
|
||||
let (device, _queue) = wgpu::Device::noop(&DeviceDescriptor {
|
||||
required_features: Features::EXTERNAL_TEXTURE,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
let texture_descriptor = TextureDescriptor {
|
||||
label: None,
|
||||
size: Extent3d {
|
||||
width: 512,
|
||||
height: 512,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: TextureDimension::D2,
|
||||
format: TextureFormat::Rgba8Unorm,
|
||||
usage: TextureUsages::TEXTURE_BINDING,
|
||||
view_formats: &[],
|
||||
};
|
||||
|
||||
let r_texture = device.create_texture(&TextureDescriptor {
|
||||
format: TextureFormat::R8Unorm,
|
||||
..texture_descriptor
|
||||
});
|
||||
let r_view = r_texture.create_view(&TextureViewDescriptor::default());
|
||||
let rg_texture = device.create_texture(&TextureDescriptor {
|
||||
format: TextureFormat::Rg8Unorm,
|
||||
..texture_descriptor
|
||||
});
|
||||
let rg_view = rg_texture.create_view(&TextureViewDescriptor::default());
|
||||
let rgba_texture = device.create_texture(&TextureDescriptor {
|
||||
format: TextureFormat::Rgba8Unorm,
|
||||
..texture_descriptor
|
||||
});
|
||||
let rgba_view = rgba_texture.create_view(&TextureViewDescriptor::default());
|
||||
|
||||
let _ = valid(&device, || {
|
||||
device.create_external_texture(
|
||||
&ExternalTextureDescriptor {
|
||||
format: ExternalTextureFormat::Rgba,
|
||||
label: None,
|
||||
width: r_texture.width(),
|
||||
height: r_texture.height(),
|
||||
yuv_conversion_matrix: [0.0; 16],
|
||||
sample_transform: [0.0; 6],
|
||||
load_transform: [0.0; 6],
|
||||
},
|
||||
&[&rgba_view],
|
||||
)
|
||||
});
|
||||
let _ = valid(&device, || {
|
||||
device.create_external_texture(
|
||||
&ExternalTextureDescriptor {
|
||||
format: ExternalTextureFormat::Nv12,
|
||||
label: None,
|
||||
width: r_texture.width(),
|
||||
height: r_texture.height(),
|
||||
yuv_conversion_matrix: [0.0; 16],
|
||||
sample_transform: [0.0; 6],
|
||||
load_transform: [0.0; 6],
|
||||
},
|
||||
&[&r_view, &rg_view],
|
||||
)
|
||||
});
|
||||
let _ = valid(&device, || {
|
||||
device.create_external_texture(
|
||||
&ExternalTextureDescriptor {
|
||||
format: ExternalTextureFormat::Yu12,
|
||||
label: None,
|
||||
width: r_texture.width(),
|
||||
height: r_texture.height(),
|
||||
yuv_conversion_matrix: [0.0; 16],
|
||||
sample_transform: [0.0; 6],
|
||||
load_transform: [0.0; 6],
|
||||
},
|
||||
&[&r_view, &r_view, &r_view],
|
||||
)
|
||||
});
|
||||
|
||||
// Wrong number of planes for format
|
||||
let _ = fail(
|
||||
&device,
|
||||
|| {
|
||||
device.create_external_texture(
|
||||
&ExternalTextureDescriptor {
|
||||
format: ExternalTextureFormat::Rgba,
|
||||
label: None,
|
||||
width: r_texture.width(),
|
||||
height: r_texture.height(),
|
||||
yuv_conversion_matrix: [0.0; 16],
|
||||
sample_transform: [0.0; 6],
|
||||
load_transform: [0.0; 6],
|
||||
},
|
||||
&[&r_view, &r_view],
|
||||
)
|
||||
},
|
||||
Some("External texture format Rgba expects 1 planes, but given 2"),
|
||||
);
|
||||
let _ = fail(
|
||||
&device,
|
||||
|| {
|
||||
device.create_external_texture(
|
||||
&ExternalTextureDescriptor {
|
||||
format: ExternalTextureFormat::Nv12,
|
||||
label: None,
|
||||
width: r_texture.width(),
|
||||
height: r_texture.height(),
|
||||
yuv_conversion_matrix: [0.0; 16],
|
||||
sample_transform: [0.0; 6],
|
||||
load_transform: [0.0; 6],
|
||||
},
|
||||
&[&r_view],
|
||||
)
|
||||
},
|
||||
Some("External texture format Nv12 expects 2 planes, but given 1"),
|
||||
);
|
||||
let _ = fail(
|
||||
&device,
|
||||
|| {
|
||||
device.create_external_texture(
|
||||
&ExternalTextureDescriptor {
|
||||
format: ExternalTextureFormat::Yu12,
|
||||
label: None,
|
||||
width: r_texture.width(),
|
||||
height: r_texture.height(),
|
||||
yuv_conversion_matrix: [0.0; 16],
|
||||
sample_transform: [0.0; 6],
|
||||
load_transform: [0.0; 6],
|
||||
},
|
||||
&[&r_view, &r_view],
|
||||
)
|
||||
},
|
||||
Some("External texture format Yu12 expects 3 planes, but given 2"),
|
||||
);
|
||||
|
||||
// Wrong plane formats
|
||||
let _ = fail(
|
||||
&device,
|
||||
|| {
|
||||
device.create_external_texture(
|
||||
&ExternalTextureDescriptor {
|
||||
format: ExternalTextureFormat::Rgba,
|
||||
label: None,
|
||||
width: r_texture.width(),
|
||||
height: r_texture.height(),
|
||||
yuv_conversion_matrix: [0.0; 16],
|
||||
sample_transform: [0.0; 6],
|
||||
load_transform: [0.0; 6],
|
||||
},
|
||||
&[&r_view],
|
||||
)
|
||||
},
|
||||
Some("External texture format Rgba plane 0 expects format with 4 components but given view with format R8Unorm (1 components)"),
|
||||
);
|
||||
let _ = fail(
|
||||
&device,
|
||||
|| {
|
||||
device.create_external_texture(
|
||||
&ExternalTextureDescriptor {
|
||||
format: ExternalTextureFormat::Nv12,
|
||||
label: None,
|
||||
width: r_texture.width(),
|
||||
height: r_texture.height(),
|
||||
yuv_conversion_matrix: [0.0; 16],
|
||||
sample_transform: [0.0; 6],
|
||||
load_transform: [0.0; 6],
|
||||
},
|
||||
&[&r_view, &rgba_view],
|
||||
)
|
||||
},
|
||||
Some("External texture format Nv12 plane 1 expects format with 2 components but given view with format Rgba8Unorm (4 components)"),
|
||||
);
|
||||
let _ = fail(
|
||||
&device,
|
||||
|| {
|
||||
device.create_external_texture(
|
||||
&ExternalTextureDescriptor {
|
||||
format: ExternalTextureFormat::Yu12,
|
||||
label: None,
|
||||
width: r_texture.width(),
|
||||
height: r_texture.height(),
|
||||
yuv_conversion_matrix: [0.0; 16],
|
||||
sample_transform: [0.0; 6],
|
||||
load_transform: [0.0; 6],
|
||||
},
|
||||
&[&r_view, &rg_view, &r_view],
|
||||
)
|
||||
},
|
||||
Some("External texture format Yu12 plane 1 expects format with 1 components but given view with format Rg8Unorm (2 components)"),
|
||||
);
|
||||
|
||||
// Wrong sample type
|
||||
let uint_texture = device.create_texture(&TextureDescriptor {
|
||||
format: TextureFormat::Rgba8Uint,
|
||||
..texture_descriptor
|
||||
});
|
||||
let uint_view = uint_texture.create_view(&TextureViewDescriptor::default());
|
||||
let _ = fail(
|
||||
&device,
|
||||
|| {
|
||||
device.create_external_texture(
|
||||
&ExternalTextureDescriptor {
|
||||
format: ExternalTextureFormat::Rgba,
|
||||
label: None,
|
||||
width: uint_texture.width(),
|
||||
height: uint_texture.height(),
|
||||
yuv_conversion_matrix: [0.0; 16],
|
||||
sample_transform: [0.0; 6],
|
||||
load_transform: [0.0; 6],
|
||||
},
|
||||
&[&uint_view],
|
||||
)
|
||||
},
|
||||
Some("External texture planes expect a filterable float sample type, but given view with format Rgba8Uint (sample type Uint)"),
|
||||
);
|
||||
|
||||
// Wrong texture dimension
|
||||
let d3_texture = device.create_texture(&TextureDescriptor {
|
||||
dimension: TextureDimension::D3,
|
||||
..texture_descriptor
|
||||
});
|
||||
let d3_view = d3_texture.create_view(&TextureViewDescriptor::default());
|
||||
let _ = fail(
|
||||
&device,
|
||||
|| {
|
||||
device.create_external_texture(
|
||||
&ExternalTextureDescriptor {
|
||||
format: ExternalTextureFormat::Rgba,
|
||||
label: None,
|
||||
width: d3_texture.width(),
|
||||
height: d3_texture.height(),
|
||||
yuv_conversion_matrix: [0.0; 16],
|
||||
sample_transform: [0.0; 6],
|
||||
load_transform: [0.0; 6],
|
||||
},
|
||||
&[&d3_view],
|
||||
)
|
||||
},
|
||||
Some("External texture planes expect 2D dimension, but given view with dimension = D3"),
|
||||
);
|
||||
|
||||
// Multisampled
|
||||
let multisampled_texture = device.create_texture(&TextureDescriptor {
|
||||
sample_count: 4,
|
||||
usage: TextureUsages::RENDER_ATTACHMENT | TextureUsages::TEXTURE_BINDING,
|
||||
..texture_descriptor
|
||||
});
|
||||
let multisampled_view = multisampled_texture.create_view(&TextureViewDescriptor::default());
|
||||
let _ = fail(
|
||||
&device,
|
||||
|| {
|
||||
device.create_external_texture(
|
||||
&ExternalTextureDescriptor {
|
||||
format: ExternalTextureFormat::Rgba,
|
||||
label: None,
|
||||
width: multisampled_texture.width(),
|
||||
height: multisampled_texture.height(),
|
||||
yuv_conversion_matrix: [0.0; 16],
|
||||
sample_transform: [0.0; 6],
|
||||
load_transform: [0.0; 6],
|
||||
},
|
||||
&[&multisampled_view],
|
||||
)
|
||||
},
|
||||
Some("External texture planes cannot be multisampled, but given view with samples = 4"),
|
||||
);
|
||||
|
||||
// Missing TEXTURE_BINDING
|
||||
let non_binding_texture = device.create_texture(&TextureDescriptor {
|
||||
usage: TextureUsages::STORAGE_BINDING,
|
||||
..texture_descriptor
|
||||
});
|
||||
let non_binding_view = non_binding_texture.create_view(&TextureViewDescriptor::default());
|
||||
let _ = fail(
|
||||
&device,
|
||||
|| {
|
||||
device.create_external_texture(
|
||||
&ExternalTextureDescriptor {
|
||||
format: ExternalTextureFormat::Rgba,
|
||||
label: None,
|
||||
width: non_binding_texture.width(),
|
||||
height: non_binding_texture.height(),
|
||||
yuv_conversion_matrix: [0.0; 16],
|
||||
sample_transform: [0.0; 6],
|
||||
load_transform: [0.0; 6],
|
||||
},
|
||||
&[&non_binding_view],
|
||||
)
|
||||
},
|
||||
Some("Usage flags TextureUsages(STORAGE_BINDING) of TextureView with '' label do not contain required usage flags TextureUsages(TEXTURE_BINDING)"),
|
||||
);
|
||||
}
|
||||
|
||||
/// Ensures an [`ExternalTexture`] can be bound to a [`BindingType::ExternalTexture`]
|
||||
/// resource binding.
|
||||
#[test]
|
||||
fn external_texture_binding() {
|
||||
let (device, _queue) = wgpu::Device::noop(&DeviceDescriptor {
|
||||
required_features: Features::EXTERNAL_TEXTURE,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
let bgl = valid(&device, || {
|
||||
device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
||||
label: None,
|
||||
entries: &[BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: ShaderStages::FRAGMENT,
|
||||
ty: BindingType::ExternalTexture,
|
||||
count: None,
|
||||
}],
|
||||
})
|
||||
});
|
||||
|
||||
let texture_descriptor = TextureDescriptor {
|
||||
label: None,
|
||||
size: Extent3d {
|
||||
width: 256,
|
||||
height: 256,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: TextureDimension::D2,
|
||||
format: TextureFormat::Rgba8Unorm,
|
||||
usage: TextureUsages::TEXTURE_BINDING,
|
||||
view_formats: &[],
|
||||
};
|
||||
let external_texture_descriptor = ExternalTextureDescriptor {
|
||||
label: None,
|
||||
width: texture_descriptor.size.width,
|
||||
height: texture_descriptor.size.height,
|
||||
format: ExternalTextureFormat::Rgba,
|
||||
yuv_conversion_matrix: [0.0; 16],
|
||||
sample_transform: [0.0; 6],
|
||||
load_transform: [0.0; 6],
|
||||
};
|
||||
|
||||
valid(&device, || {
|
||||
let texture = device.create_texture(&texture_descriptor);
|
||||
let view = texture.create_view(&TextureViewDescriptor::default());
|
||||
let external_texture =
|
||||
device.create_external_texture(&external_texture_descriptor, &[&view]);
|
||||
|
||||
device.create_bind_group(&BindGroupDescriptor {
|
||||
label: None,
|
||||
layout: &bgl,
|
||||
entries: &[BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: BindingResource::ExternalTexture(&external_texture),
|
||||
}],
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/// Ensures a [`TextureView`] can be bound to a [`BindingType::ExternalTexture`]
|
||||
/// resource binding.
|
||||
#[test]
|
||||
@ -160,3 +519,139 @@ fn external_texture_binding_texture_view() {
|
||||
Some("Texture binding 0 expects multisampled = false, but given a view with samples = 4"),
|
||||
);
|
||||
}
|
||||
|
||||
/// Ensures that submitting a command buffer referencing an external texture, any of
|
||||
/// whose plane textures have already been destroyed, results in an error.
|
||||
#[test]
|
||||
fn destroyed_external_texture_plane() {
|
||||
let (device, queue) = wgpu::Device::noop(&DeviceDescriptor {
|
||||
required_features: Features::EXTERNAL_TEXTURE,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
let target_texture = device.create_texture(&TextureDescriptor {
|
||||
label: None,
|
||||
size: Extent3d {
|
||||
width: 512,
|
||||
height: 512,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: TextureDimension::D2,
|
||||
format: TextureFormat::Rgba8Unorm,
|
||||
usage: TextureUsages::RENDER_ATTACHMENT,
|
||||
view_formats: &[],
|
||||
});
|
||||
let target_view = target_texture.create_view(&TextureViewDescriptor::default());
|
||||
|
||||
let plane_texture = device.create_texture(&TextureDescriptor {
|
||||
label: Some("External texture plane"),
|
||||
size: Extent3d {
|
||||
width: 512,
|
||||
height: 512,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: TextureDimension::D2,
|
||||
format: TextureFormat::Rgba8Unorm,
|
||||
usage: TextureUsages::TEXTURE_BINDING,
|
||||
view_formats: &[],
|
||||
});
|
||||
let plane_view = plane_texture.create_view(&TextureViewDescriptor::default());
|
||||
|
||||
let external_texture = device.create_external_texture(
|
||||
&ExternalTextureDescriptor {
|
||||
format: ExternalTextureFormat::Rgba,
|
||||
label: None,
|
||||
width: plane_texture.width(),
|
||||
height: plane_texture.height(),
|
||||
yuv_conversion_matrix: [0.0; 16],
|
||||
sample_transform: [0.0; 6],
|
||||
load_transform: [0.0; 6],
|
||||
},
|
||||
&[&plane_view],
|
||||
);
|
||||
|
||||
let module = device.create_shader_module(ShaderModuleDescriptor {
|
||||
label: None,
|
||||
source: ShaderSource::Wgsl(std::borrow::Cow::Borrowed(
|
||||
"
|
||||
@group(0) @binding(0)
|
||||
var tex: texture_external;
|
||||
@vertex fn vert_main() -> @builtin(position) vec4<f32> { return vec4<f32>(0); }
|
||||
@fragment fn frag_main() -> @location(0) vec4<f32> { return textureLoad(tex, vec2(0)); }",
|
||||
)),
|
||||
});
|
||||
|
||||
let pipeline = device.create_render_pipeline(&RenderPipelineDescriptor {
|
||||
label: None,
|
||||
layout: None,
|
||||
vertex: VertexState {
|
||||
module: &module,
|
||||
entry_point: None,
|
||||
compilation_options: PipelineCompilationOptions::default(),
|
||||
buffers: &[],
|
||||
},
|
||||
primitive: PrimitiveState::default(),
|
||||
depth_stencil: None,
|
||||
multisample: MultisampleState::default(),
|
||||
fragment: Some(FragmentState {
|
||||
module: &module,
|
||||
entry_point: None,
|
||||
compilation_options: PipelineCompilationOptions::default(),
|
||||
targets: &[Some(ColorTargetState {
|
||||
format: target_texture.format(),
|
||||
blend: None,
|
||||
write_mask: ColorWrites::ALL,
|
||||
})],
|
||||
}),
|
||||
multiview: None,
|
||||
cache: None,
|
||||
});
|
||||
|
||||
let bind_group = device.create_bind_group(&BindGroupDescriptor {
|
||||
label: None,
|
||||
layout: &pipeline.get_bind_group_layout(0),
|
||||
entries: &[BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: BindingResource::ExternalTexture(&external_texture),
|
||||
}],
|
||||
});
|
||||
|
||||
let mut encoder = device.create_command_encoder(&CommandEncoderDescriptor { label: None });
|
||||
let mut pass = encoder.begin_render_pass(&RenderPassDescriptor {
|
||||
label: None,
|
||||
color_attachments: &[Some(RenderPassColorAttachment {
|
||||
view: &target_view,
|
||||
depth_slice: None,
|
||||
resolve_target: None,
|
||||
ops: Operations {
|
||||
load: LoadOp::Clear(Color {
|
||||
r: 0.0,
|
||||
g: 0.0,
|
||||
b: 0.0,
|
||||
a: 1.0,
|
||||
}),
|
||||
store: StoreOp::Store,
|
||||
},
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
timestamp_writes: None,
|
||||
occlusion_query_set: None,
|
||||
});
|
||||
|
||||
pass.set_pipeline(&pipeline);
|
||||
pass.set_bind_group(0, &bind_group, &[]);
|
||||
pass.draw(0..0, 0..0);
|
||||
drop(pass);
|
||||
|
||||
plane_texture.destroy();
|
||||
|
||||
fail(
|
||||
&device,
|
||||
|| queue.submit([encoder.finish()]),
|
||||
Some("Texture with 'External texture plane' label has been destroyed"),
|
||||
);
|
||||
}
|
||||
|
||||
@ -2665,11 +2665,7 @@ impl Device {
|
||||
|
||||
used.buffers
|
||||
.insert_single(external_texture.params.clone(), wgt::BufferUses::UNIFORM);
|
||||
let params = hal::BufferBinding {
|
||||
buffer: external_texture.params.try_raw(snatch_guard)?,
|
||||
offset: 0,
|
||||
size: wgt::BufferSize::new(external_texture.params.size),
|
||||
};
|
||||
let params = external_texture.params.binding(0, None, snatch_guard)?.0;
|
||||
|
||||
Ok(hal::ExternalTextureBinding { planes, params })
|
||||
}
|
||||
@ -2718,11 +2714,11 @@ impl Device {
|
||||
usage: internal_use,
|
||||
},
|
||||
];
|
||||
let params = hal::BufferBinding {
|
||||
buffer: self.default_external_texture_params_buffer.as_ref(),
|
||||
offset: 0,
|
||||
size: None,
|
||||
};
|
||||
let params = hal::BufferBinding::new_unchecked(
|
||||
self.default_external_texture_params_buffer.as_ref(),
|
||||
0,
|
||||
None,
|
||||
);
|
||||
|
||||
Ok(hal::ExternalTextureBinding { planes, params })
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user