[wgpu-core] Add ExternalTexture type to wgpu-core

`ExternalTexture` will form the basis of wgpu's implementation of
WebGPU's `GPUExternalTexture`. [1]

The application will be responsible for creating `Texture`(s) and
`TextureView`(s) from the external texture source and managing their
lifecycle. It may have a single RGBA texture, or it may have multiple
textures for separate Y and Cb/Cr planes. It can then create an external
texture by calling `create_external_texture()`, providing the texture
views and a descriptor. The descriptor provides the following required
information:

  * Whether the texture data is RGBA, or multiplanar or interleaved
    YCbCr.
  * The purpoted size of the external texture, which may not match the
    actual size of the underlying textures.
  * A matrix for converting from YCbCr to RGBA, if required.
  * A transform to apply to texture sample coordinates, allowing for
    rotation and crop rects.

The external texture stores a reference to the provided texture views,
and additionally owns a `Buffer`. This buffer holds data of the type
`ExternalTextureParams`, and will be provided as a uniform buffer to
shaders containing external textures. This contains information that
will be required by the shaders to handle external textures correctly.

Note that attempting to create an external texture will fail unless the
`Feature::EXTERNAL_TEXTURE` feature is enabled, which as of yet is not
supported by any HAL backends.

Additionally add the relevant API to wgpu, implemented for the
wgpu-core backend. The web and custom backends are unimplemented.

[1] https://www.w3.org/TR/webgpu/#gpuexternaltexture
This commit is contained in:
Jamie Nicol 2025-05-27 13:15:03 +01:00 committed by Jim Blandy
parent 43a4d53107
commit acfa8c5776
19 changed files with 592 additions and 28 deletions

View File

@ -184,6 +184,14 @@ impl DeviceInterface for CustomDevice {
unimplemented!()
}
fn create_external_texture(
&self,
_desc: &wgpu::ExternalTextureDescriptor<'_>,
_planes: &[&wgpu::TextureView],
) -> wgpu::custom::DispatchExternalTexture {
unimplemented!()
}
fn create_blas(
&self,
_desc: &wgpu::CreateBlasDescriptor<'_>,

View File

@ -244,6 +244,19 @@ impl GlobalPlay for wgc::global::Global {
Action::DestroyTextureView(id) => {
self.texture_view_drop(id).unwrap();
}
Action::CreateExternalTexture { id, desc, planes } => {
let (_, error) =
self.device_create_external_texture(device, &desc, &planes, Some(id));
if let Some(e) = error {
panic!("{e}");
}
}
Action::FreeExternalTexture(id) => {
self.external_texture_destroy(id);
}
Action::DestroyExternalTexture(id) => {
self.external_texture_drop(id);
}
Action::CreateSampler(id, desc) => {
let (_, error) = self.device_create_sampler(device, &desc, Some(id));
if let Some(e) = error {

View File

@ -72,14 +72,7 @@ observe_locks = ["std", "dep:ron", "serde/serde_derive"]
serde = ["dep:serde", "wgpu-types/serde", "arrayvec/serde", "hashbrown/serde"]
## Enable API tracing.
trace = [
"serde",
"std",
"dep:ron",
"naga/serialize",
"wgpu-types/trace",
"dep:bytemuck",
]
trace = ["serde", "std", "dep:ron", "naga/serialize", "wgpu-types/trace"]
## Enable API replaying
replay = ["serde", "naga/deserialize"]
@ -100,7 +93,7 @@ wgsl = ["naga/wgsl-in"]
glsl = ["naga/glsl-in"]
## Enable `ShaderModuleSource::SpirV`
spirv = ["naga/spv-in", "dep:bytemuck"]
spirv = ["naga/spv-in"]
#! ### Other
# --------------------------------------------------------------------
@ -180,7 +173,7 @@ arrayvec.workspace = true
bit-vec.workspace = true
bit-set.workspace = true
bitflags.workspace = true
bytemuck = { workspace = true, optional = true }
bytemuck.workspace = true
document-features.workspace = true
hashbrown.workspace = true
indexmap.workspace = true

View File

@ -178,6 +178,9 @@ impl Global {
fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));
}
/// Assign `id_in` an error with the given `label`.
///
/// See [`Self::create_buffer_error`] for more context and explanation.
pub fn create_render_bundle_error(
&self,
id_in: Option<id::RenderBundleId>,
@ -189,7 +192,7 @@ impl Global {
/// Assign `id_in` an error with the given `label`.
///
/// See `create_buffer_error` for more context and explanation.
/// See [`Self::create_buffer_error`] for more context and explanation.
pub fn create_texture_error(
&self,
id_in: Option<id::TextureId>,
@ -199,6 +202,18 @@ impl Global {
fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));
}
/// Assign `id_in` an error with the given `label`.
///
/// See [`Self::create_buffer_error`] for more context and explanation.
pub fn create_external_texture_error(
&self,
id_in: Option<id::ExternalTextureId>,
desc: &resource::ExternalTextureDescriptor,
) {
let fid = self.hub.external_textures.prepare(id_in);
fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));
}
#[cfg(feature = "replay")]
pub fn device_set_buffer_data(
&self,
@ -512,6 +527,94 @@ impl Global {
Ok(())
}
pub fn device_create_external_texture(
&self,
device_id: DeviceId,
desc: &resource::ExternalTextureDescriptor,
planes: &[id::TextureViewId],
id_in: Option<id::ExternalTextureId>,
) -> (
id::ExternalTextureId,
Option<resource::CreateExternalTextureError>,
) {
profiling::scope!("Device::create_external_texture");
let hub = &self.hub;
let fid = hub.external_textures.prepare(id_in);
let error = 'error: {
let device = self.hub.devices.get(device_id);
#[cfg(feature = "trace")]
if let Some(ref mut trace) = *device.trace.lock() {
let planes = Box::from(planes);
trace.add(trace::Action::CreateExternalTexture {
id: fid.id(),
desc: desc.clone(),
planes,
});
}
let planes = planes
.iter()
.map(|plane_id| self.hub.texture_views.get(*plane_id).get())
.collect::<Result<Vec<_>, _>>();
let planes = match planes {
Ok(planes) => planes,
Err(error) => break 'error error.into(),
};
let external_texture = match device.create_external_texture(desc, &planes) {
Ok(external_texture) => external_texture,
Err(error) => break 'error error,
};
let id = fid.assign(Fallible::Valid(external_texture));
api_log!("Device::create_external_texture({desc:?}) -> {id:?}");
return (id, None);
};
let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));
(id, Some(error))
}
pub fn external_texture_destroy(&self, external_texture_id: id::ExternalTextureId) {
profiling::scope!("ExternalTexture::destroy");
api_log!("ExternalTexture::destroy {external_texture_id:?}");
let hub = &self.hub;
let Ok(external_texture) = hub.external_textures.get(external_texture_id).get() else {
// If the external texture is already invalid, there's nothing to do.
return;
};
#[cfg(feature = "trace")]
if let Some(trace) = external_texture.device.trace.lock().as_mut() {
trace.add(trace::Action::FreeExternalTexture(external_texture_id));
}
external_texture.destroy();
}
pub fn external_texture_drop(&self, external_texture_id: id::ExternalTextureId) {
profiling::scope!("ExternalTexture::drop");
api_log!("ExternalTexture::drop {external_texture_id:?}");
let hub = &self.hub;
let _external_texture = hub.external_textures.remove(external_texture_id);
#[cfg(feature = "trace")]
if let Ok(external_texture) = _external_texture.get() {
if let Some(t) = external_texture.device.trace.lock().as_mut() {
t.add(trace::Action::DestroyExternalTexture(external_texture_id));
}
}
}
pub fn device_create_sampler(
&self,
device_id: DeviceId,

View File

@ -41,8 +41,9 @@ use crate::{
pipeline,
pool::ResourcePool,
resource::{
self, Buffer, Fallible, Labeled, ParentDevice, QuerySet, RawResourceAccess, Sampler,
StagingBuffer, Texture, TextureView, TextureViewNotRenderableReason, Tlas, TrackingData,
self, Buffer, ExternalTexture, Fallible, Labeled, ParentDevice, QuerySet,
RawResourceAccess, Sampler, StagingBuffer, Texture, TextureView,
TextureViewNotRenderableReason, Tlas, TrackingData,
},
resource_log,
snatch::{SnatchGuard, SnatchLock, Snatchable},
@ -75,6 +76,42 @@ pub(crate) struct CommandIndices {
pub(crate) next_acceleration_structure_build_command_index: u64,
}
/// Parameters provided to shaders via a uniform buffer, describing an
/// ExternalTexture resource binding.
#[repr(C)]
#[derive(Copy, Clone, bytemuck::Zeroable, bytemuck::Pod)]
pub struct ExternalTextureParams {
/// 4x4 column-major matrix with which to convert sampled YCbCr values
/// to RGBA.
/// This is ignored when `num_planes` is 1.
pub yuv_conversion_matrix: [f32; 16],
/// 3x2 column-major matrix with which to multiply texture coordinates
/// prior to sampling from the external texture.
pub sample_transform: [f32; 6],
pub load_transform: [f32; 6],
/// Size of the external texture. This value should be returned by size
/// queries in shader code. Note that this may not match the dimensions of
/// the underlying texture(s). A value of [0, 0] indicates that the actual
/// size of plane 0 should be used.
pub size: [u32; 2],
/// Number of planes. 1 indicates a single RGBA plane. 2 indicates a Y
/// plane and an interleaved CbCr plane. 3 indicates separate Y, Cb, and Cr
/// planes.
pub num_planes: u32,
}
impl ExternalTextureParams {
pub fn from_desc<L>(desc: &wgt::ExternalTextureDescriptor<L>) -> Self {
Self {
yuv_conversion_matrix: desc.yuv_conversion_matrix,
size: [desc.width, desc.height],
sample_transform: desc.sample_transform,
load_transform: desc.load_transform,
num_planes: desc.num_planes() as u32,
}
}
}
/// Structure describing a logical device. Some members are internally mutable,
/// stored behind mutexes.
pub struct Device {
@ -1550,6 +1587,101 @@ impl Device {
Ok(view)
}
pub(crate) fn create_external_texture(
self: &Arc<Self>,
desc: &resource::ExternalTextureDescriptor,
planes: &[Arc<TextureView>],
) -> Result<Arc<ExternalTexture>, resource::CreateExternalTextureError> {
use resource::CreateExternalTextureError;
self.require_features(wgt::Features::EXTERNAL_TEXTURE)?;
self.check_is_valid()?;
if desc.num_planes() != planes.len() {
return Err(CreateExternalTextureError::IncorrectPlaneCount {
format: desc.format,
expected: desc.num_planes(),
provided: planes.len(),
});
}
let planes = planes
.iter()
.enumerate()
.map(|(i, plane)| {
if plane.samples != 1 {
return Err(CreateExternalTextureError::InvalidPlaneMultisample(
plane.samples,
));
}
let sample_type = plane
.desc
.format
.sample_type(Some(plane.desc.range.aspect), Some(self.features))
.unwrap();
if !matches!(sample_type, TextureSampleType::Float { filterable: true }) {
return Err(CreateExternalTextureError::InvalidPlaneSampleType {
format: plane.desc.format,
sample_type,
});
}
if plane.desc.dimension != TextureViewDimension::D2 {
return Err(CreateExternalTextureError::InvalidPlaneDimension(
plane.desc.dimension,
));
}
let expected_components = match desc.format {
wgt::ExternalTextureFormat::Rgba => 4,
wgt::ExternalTextureFormat::Nv12 => match i {
0 => 1,
1 => 2,
_ => unreachable!(),
},
wgt::ExternalTextureFormat::Yu12 => 1,
};
if plane.desc.format.components() != expected_components {
return Err(CreateExternalTextureError::InvalidPlaneFormat {
format: desc.format,
plane: i,
expected: expected_components,
provided: plane.desc.format,
});
}
plane.check_usage(wgt::TextureUsages::TEXTURE_BINDING)?;
Ok(plane.clone())
})
.collect::<Result<_, _>>()?;
let params_data = ExternalTextureParams::from_desc(desc);
let label = desc.label.as_ref().map(|l| alloc::format!("{l} params"));
let params_desc = resource::BufferDescriptor {
label: label.map(Cow::Owned),
size: size_of_val(&params_data) as wgt::BufferAddress,
usage: wgt::BufferUsages::UNIFORM | wgt::BufferUsages::COPY_DST,
mapped_at_creation: false,
};
let params = self.create_buffer(&params_desc)?;
self.get_queue().unwrap().write_buffer(
Fallible::Valid(params.clone()),
0,
bytemuck::bytes_of(&params_data),
)?;
let external_texture = ExternalTexture {
device: self.clone(),
planes,
params,
label: desc.label.to_string(),
tracking_data: TrackingData::new(self.tracker_indices.external_textures.clone()),
};
let external_texture = Arc::new(external_texture);
Ok(external_texture)
}
pub(crate) fn create_sampler(
self: &Arc<Self>,
desc: &resource::SamplerDescriptor,

View File

@ -58,6 +58,13 @@ pub enum Action<'a> {
desc: crate::resource::TextureViewDescriptor<'a>,
},
DestroyTextureView(id::TextureViewId),
CreateExternalTexture {
id: id::ExternalTextureId,
desc: crate::resource::ExternalTextureDescriptor<'a>,
planes: alloc::boxed::Box<[id::TextureViewId]>,
},
FreeExternalTexture(id::ExternalTextureId),
DestroyExternalTexture(id::ExternalTextureId),
CreateSampler(id::SamplerId, crate::resource::SamplerDescriptor<'a>),
DestroySampler(id::SamplerId),
GetSurfaceTexture {

View File

@ -111,7 +111,8 @@ use crate::{
pipeline::{ComputePipeline, PipelineCache, RenderPipeline, ShaderModule},
registry::{Registry, RegistryReport},
resource::{
Blas, Buffer, Fallible, QuerySet, Sampler, StagingBuffer, Texture, TextureView, Tlas,
Blas, Buffer, ExternalTexture, Fallible, QuerySet, Sampler, StagingBuffer, Texture,
TextureView, Tlas,
},
};
@ -134,6 +135,7 @@ pub struct HubReport {
pub buffers: RegistryReport,
pub textures: RegistryReport,
pub texture_views: RegistryReport,
pub external_textures: RegistryReport,
pub samplers: RegistryReport,
}
@ -183,6 +185,7 @@ pub struct Hub {
pub(crate) staging_buffers: Registry<StagingBuffer>,
pub(crate) textures: Registry<Fallible<Texture>>,
pub(crate) texture_views: Registry<Fallible<TextureView>>,
pub(crate) external_textures: Registry<Fallible<ExternalTexture>>,
pub(crate) samplers: Registry<Fallible<Sampler>>,
pub(crate) blas_s: Registry<Fallible<Blas>>,
pub(crate) tlas_s: Registry<Fallible<Tlas>>,
@ -209,6 +212,7 @@ impl Hub {
staging_buffers: Registry::new(),
textures: Registry::new(),
texture_views: Registry::new(),
external_textures: Registry::new(),
samplers: Registry::new(),
blas_s: Registry::new(),
tlas_s: Registry::new(),
@ -234,6 +238,7 @@ impl Hub {
buffers: self.buffers.generate_report(),
textures: self.textures.generate_report(),
texture_views: self.texture_views.generate_report(),
external_textures: self.external_textures.generate_report(),
samplers: self.samplers.generate_report(),
}
}

View File

@ -261,6 +261,7 @@ ids! {
pub type StagingBufferId StagingBuffer;
pub type TextureViewId TextureView;
pub type TextureId Texture;
pub type ExternalTextureId ExternalTexture;
pub type SamplerId Sampler;
pub type BindGroupLayoutId BindGroupLayout;
pub type PipelineLayoutId PipelineLayout;

View File

@ -1795,6 +1795,102 @@ crate::impl_parent_device!(TextureView);
crate::impl_storage_item!(TextureView);
crate::impl_trackable!(TextureView);
pub type ExternalTextureDescriptor<'a> = wgt::ExternalTextureDescriptor<Label<'a>>;
#[derive(Debug)]
pub struct ExternalTexture {
pub(crate) device: Arc<Device>,
/// Between 1 and 3 (inclusive) planes of texture data.
#[allow(dead_code)]
pub(crate) planes: arrayvec::ArrayVec<Arc<TextureView>, 3>,
/// Buffer containing a [`crate::device::resource::ExternalTextureParams`]
/// describing the external texture.
#[allow(dead_code)]
pub(crate) params: Arc<Buffer>,
/// The `label` from the descriptor used to create the resource.
pub(crate) label: String,
pub(crate) tracking_data: TrackingData,
}
impl Drop for ExternalTexture {
fn drop(&mut self) {
resource_log!("Destroy raw {}", self.error_ident());
}
}
impl ExternalTexture {
pub(crate) fn destroy(self: &Arc<Self>) {
self.params.destroy();
}
}
#[derive(Clone, Debug, Error)]
#[non_exhaustive]
pub enum CreateExternalTextureError {
#[error(transparent)]
Device(#[from] DeviceError),
#[error(transparent)]
MissingFeatures(#[from] MissingFeatures),
#[error(transparent)]
InvalidResource(#[from] InvalidResourceError),
#[error(transparent)]
CreateBuffer(#[from] CreateBufferError),
#[error(transparent)]
QueueWrite(#[from] queue::QueueWriteError),
#[error("External texture format {format:?} expects {expected} planes, but given {provided}")]
IncorrectPlaneCount {
format: wgt::ExternalTextureFormat,
expected: usize,
provided: usize,
},
#[error("External texture planes cannot be multisampled, but given view with samples = {0}")]
InvalidPlaneMultisample(u32),
#[error("External texture planes expect a filterable float sample type, but given view with format {format:?} (sample type {sample_type:?})")]
InvalidPlaneSampleType {
format: wgt::TextureFormat,
sample_type: wgt::TextureSampleType,
},
#[error("External texture planes expect 2D dimension, but given view with dimension = {0:?}")]
InvalidPlaneDimension(wgt::TextureViewDimension),
#[error(transparent)]
MissingTextureUsage(#[from] MissingTextureUsageError),
#[error("External texture format {format:?} plane {plane} expects format with {expected} components but given view with format {provided:?} ({} components)",
provided.components())]
InvalidPlaneFormat {
format: wgt::ExternalTextureFormat,
plane: usize,
expected: u8,
provided: wgt::TextureFormat,
},
}
impl WebGpuError for CreateExternalTextureError {
fn webgpu_error_type(&self) -> ErrorType {
let e: &dyn WebGpuError = match self {
CreateExternalTextureError::Device(e) => e,
CreateExternalTextureError::MissingFeatures(e) => e,
CreateExternalTextureError::InvalidResource(e) => e,
CreateExternalTextureError::CreateBuffer(e) => e,
CreateExternalTextureError::QueueWrite(e) => e,
CreateExternalTextureError::MissingTextureUsage(e) => e,
CreateExternalTextureError::IncorrectPlaneCount { .. }
| CreateExternalTextureError::InvalidPlaneMultisample(_)
| CreateExternalTextureError::InvalidPlaneSampleType { .. }
| CreateExternalTextureError::InvalidPlaneDimension(_)
| CreateExternalTextureError::InvalidPlaneFormat { .. } => {
return ErrorType::Validation
}
};
e.webgpu_error_type()
}
}
crate::impl_resource_type!(ExternalTexture);
crate::impl_labeled!(ExternalTexture);
crate::impl_parent_device!(ExternalTexture);
crate::impl_storage_item!(ExternalTexture);
crate::impl_trackable!(ExternalTexture);
/// Describes a [`Sampler`]
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]

View File

@ -225,6 +225,7 @@ pub(crate) struct TrackerIndexAllocators {
pub buffers: Arc<SharedTrackerIndexAllocator>,
pub textures: Arc<SharedTrackerIndexAllocator>,
pub texture_views: Arc<SharedTrackerIndexAllocator>,
pub external_textures: Arc<SharedTrackerIndexAllocator>,
pub samplers: Arc<SharedTrackerIndexAllocator>,
pub bind_groups: Arc<SharedTrackerIndexAllocator>,
pub compute_pipelines: Arc<SharedTrackerIndexAllocator>,
@ -241,6 +242,7 @@ impl TrackerIndexAllocators {
buffers: Arc::new(SharedTrackerIndexAllocator::new()),
textures: Arc::new(SharedTrackerIndexAllocator::new()),
texture_views: Arc::new(SharedTrackerIndexAllocator::new()),
external_textures: Arc::new(SharedTrackerIndexAllocator::new()),
samplers: Arc::new(SharedTrackerIndexAllocator::new()),
bind_groups: Arc::new(SharedTrackerIndexAllocator::new()),
compute_pipelines: Arc::new(SharedTrackerIndexAllocator::new()),

View File

@ -6202,6 +6202,77 @@ impl<L, V> TextureDescriptor<L, V> {
}
}
/// Format of an `ExternalTexture`. This indicates the number of underlying
/// planes used by the `ExternalTexture` as well as each plane's format.
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ExternalTextureFormat {
/// Single [`TextureFormat::Rgba8Unorm`] or [`TextureFormat::Bgra8Unorm`] format plane.
Rgba,
/// [`TextureFormat::R8Unorm`] Y plane, and [`TextureFormat::Rg8Unorm`]
/// interleaved CbCr plane.
Nv12,
/// Separate [`TextureFormat::R8Unorm`] Y, Cb, and Cr planes.
Yu12,
}
/// Describes an [`ExternalTexture`](../wgpu/struct.ExternalTexture.html).
///
/// Corresponds to [WebGPU `GPUExternalTextureDescriptor`](
/// https://gpuweb.github.io/gpuweb/#dictdef-gpuexternaltexturedescriptor).
#[repr(C)]
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ExternalTextureDescriptor<L> {
/// Debug label of the external texture. This will show up in graphics
/// debuggers for easy identification.
pub label: L,
/// Width of the external texture. Note that both this and `height` may
/// not match the dimensions of the underlying texture(s). This could be
/// due to a crop rect or rotation.
pub width: u32,
/// Height of the external texture.
pub height: u32,
/// Format of the external texture.
pub format: ExternalTextureFormat,
/// 4x4 column-major matrix with which to convert sampled YCbCr values
/// to RGBA.
/// This is ignored when `format` is [`ExternalTextureFormat::Rgba`].
pub yuv_conversion_matrix: [f32; 16],
/// 3x2 column-major matrix with which to multiply normalized texture
/// coordinates prior to sampling from the external texture.
pub sample_transform: [f32; 6],
/// 3x2 column-major matrix with which to multiply unnormalized texture
/// coordinates prior to loading from the external texture.
pub load_transform: [f32; 6],
}
impl<L> ExternalTextureDescriptor<L> {
/// Takes a closure and maps the label of the external texture descriptor into another.
#[must_use]
pub fn map_label<K>(&self, fun: impl FnOnce(&L) -> K) -> ExternalTextureDescriptor<K> {
ExternalTextureDescriptor {
label: fun(&self.label),
width: self.width,
height: self.height,
format: self.format,
yuv_conversion_matrix: self.yuv_conversion_matrix,
sample_transform: self.sample_transform,
load_transform: self.load_transform,
}
}
/// The number of underlying planes used by the external texture.
pub fn num_planes(&self) -> usize {
match self.format {
ExternalTextureFormat::Rgba => 1,
ExternalTextureFormat::Nv12 => 2,
ExternalTextureFormat::Yu12 => 3,
}
}
}
/// Describes a `Sampler`.
///
/// For use with `Device::create_sampler`.

View File

@ -326,6 +326,20 @@ impl Device {
}
}
/// Creates a new [`ExternalTexture`].
#[must_use]
pub fn create_external_texture(
&self,
desc: &ExternalTextureDescriptor<'_>,
planes: &[&TextureView],
) -> ExternalTexture {
let external_texture = self.inner.create_external_texture(desc, planes);
ExternalTexture {
inner: external_texture,
}
}
/// Creates a [`Buffer`] from a wgpu-hal Buffer.
///
/// # Types

View File

@ -0,0 +1,31 @@
use crate::*;
/// Handle to an external texture on the GPU.
///
/// It can be created with [`Device::create_external_texture`].
///
/// Corresponds to [WebGPU `GPUExternalTexture`](https://gpuweb.github.io/gpuweb/#gpuexternaltexture).
#[derive(Debug, Clone)]
pub struct ExternalTexture {
pub(crate) inner: dispatch::DispatchExternalTexture,
}
#[cfg(send_sync)]
static_assertions::assert_impl_all!(ExternalTexture: Send, Sync);
crate::cmp::impl_eq_ord_hash_proxy!(ExternalTexture => .inner);
impl ExternalTexture {
/// Destroy the associated native resources as soon as possible.
pub fn destroy(&self) {
self.inner.destroy();
}
}
/// Describes an [`ExternalTexture`].
///
/// For use with [`Device::create_external_texture`].
///
/// Corresponds to [WebGPU `GPUExternalTextureDescriptor`](
/// https://gpuweb.github.io/gpuweb/#dictdef-gpuexternaltexturedescriptor).
pub type ExternalTextureDescriptor<'a> = wgt::ExternalTextureDescriptor<Label<'a>>;
static_assertions::assert_impl_all!(ExternalTextureDescriptor<'_>: Send, Sync);

View File

@ -32,6 +32,7 @@ mod common_pipeline;
mod compute_pass;
mod compute_pipeline;
mod device;
mod external_texture;
mod instance;
mod pipeline_cache;
mod pipeline_layout;
@ -60,6 +61,7 @@ pub use common_pipeline::*;
pub use compute_pass::*;
pub use compute_pipeline::*;
pub use device::*;
pub use external_texture::*;
pub use instance::*;
pub use pipeline_cache::*;
pub use pipeline_layout::*;

View File

@ -79,6 +79,7 @@ dyn_type!(pub ref struct DynTextureView(dyn TextureViewInterface));
dyn_type!(pub ref struct DynSampler(dyn SamplerInterface));
dyn_type!(pub ref struct DynBuffer(dyn BufferInterface));
dyn_type!(pub ref struct DynTexture(dyn TextureInterface));
dyn_type!(pub ref struct DynExternalTexture(dyn ExternalTextureInterface));
dyn_type!(pub ref struct DynBlas(dyn BlasInterface));
dyn_type!(pub ref struct DynTlas(dyn TlasInterface));
dyn_type!(pub ref struct DynQuerySet(dyn QuerySetInterface));

View File

@ -1259,6 +1259,12 @@ pub struct WebTexture {
ident: crate::cmp::Identifier,
}
#[derive(Debug)]
pub struct WebExternalTexture {
/// Unique identifier for this ExternalTexture.
ident: crate::cmp::Identifier,
}
#[derive(Debug)]
pub struct WebBlas {
/// Unique identifier for this Blas.
@ -1392,6 +1398,7 @@ impl_send_sync!(WebTextureView);
impl_send_sync!(WebSampler);
impl_send_sync!(WebBuffer);
impl_send_sync!(WebTexture);
impl_send_sync!(WebExternalTexture);
impl_send_sync!(WebBlas);
impl_send_sync!(WebTlas);
impl_send_sync!(WebQuerySet);
@ -1421,6 +1428,7 @@ crate::cmp::impl_eq_ord_hash_proxy!(WebTextureView => .ident);
crate::cmp::impl_eq_ord_hash_proxy!(WebSampler => .ident);
crate::cmp::impl_eq_ord_hash_proxy!(WebBuffer => .ident);
crate::cmp::impl_eq_ord_hash_proxy!(WebTexture => .ident);
crate::cmp::impl_eq_ord_hash_proxy!(WebExternalTexture => .ident);
crate::cmp::impl_eq_ord_hash_proxy!(WebBlas => .ident);
crate::cmp::impl_eq_ord_hash_proxy!(WebTlas => .ident);
crate::cmp::impl_eq_ord_hash_proxy!(WebQuerySet => .ident);
@ -2243,6 +2251,14 @@ impl dispatch::DeviceInterface for WebDevice {
.into()
}
fn create_external_texture(
&self,
_desc: &crate::ExternalTextureDescriptor<'_>,
_planes: &[&crate::TextureView],
) -> dispatch::DispatchExternalTexture {
unimplemented!("ExternalTexture not implemented for web");
}
fn create_blas(
&self,
_desc: &crate::CreateBlasDescriptor<'_>,
@ -2751,6 +2767,17 @@ impl Drop for WebTexture {
}
}
impl dispatch::ExternalTextureInterface for WebExternalTexture {
fn destroy(&self) {
unimplemented!("ExternalTexture not implemented for web");
}
}
impl Drop for WebExternalTexture {
fn drop(&mut self) {
unimplemented!("ExternalTexture not implemented for web");
}
}
impl dispatch::BlasInterface for WebBlas {
fn prepare_compact_async(&self, _callback: BlasCompactCallback) {
unimplemented!("Raytracing not implemented for web")

View File

@ -496,6 +496,12 @@ pub struct CoreTextureView {
id: wgc::id::TextureViewId,
}
#[derive(Debug)]
pub struct CoreExternalTexture {
pub(crate) context: ContextWgpuCore,
id: wgc::id::ExternalTextureId,
}
#[derive(Debug)]
pub struct CoreSampler {
pub(crate) context: ContextWgpuCore,
@ -728,6 +734,7 @@ crate::cmp::impl_eq_ord_hash_proxy!(CoreTextureView => .id);
crate::cmp::impl_eq_ord_hash_proxy!(CoreSampler => .id);
crate::cmp::impl_eq_ord_hash_proxy!(CoreBuffer => .id);
crate::cmp::impl_eq_ord_hash_proxy!(CoreTexture => .id);
crate::cmp::impl_eq_ord_hash_proxy!(CoreExternalTexture => .id);
crate::cmp::impl_eq_ord_hash_proxy!(CoreBlas => .id);
crate::cmp::impl_eq_ord_hash_proxy!(CoreTlas => .id);
crate::cmp::impl_eq_ord_hash_proxy!(CoreQuerySet => .id);
@ -1472,6 +1479,36 @@ impl dispatch::DeviceInterface for CoreDevice {
.into()
}
fn create_external_texture(
&self,
desc: &crate::ExternalTextureDescriptor<'_>,
planes: &[&crate::TextureView],
) -> dispatch::DispatchExternalTexture {
let wgt_desc = desc.map_label(|l| l.map(Borrowed));
let planes = planes
.iter()
.map(|plane| plane.inner.as_core().id)
.collect::<Vec<_>>();
let (id, error) = self
.context
.0
.device_create_external_texture(self.id, &wgt_desc, &planes, None);
if let Some(cause) = error {
self.context.handle_error(
&self.error_sink,
cause,
desc.label,
"Device::create_external_texture",
);
}
CoreExternalTexture {
context: self.context.clone(),
id,
}
.into()
}
fn create_blas(
&self,
desc: &crate::CreateBlasDescriptor<'_>,
@ -1938,6 +1975,18 @@ impl Drop for CoreTextureView {
}
}
impl dispatch::ExternalTextureInterface for CoreExternalTexture {
fn destroy(&self) {
self.context.0.external_texture_destroy(self.id);
}
}
impl Drop for CoreExternalTexture {
fn drop(&mut self) {
self.context.0.external_texture_drop(self.id);
}
}
impl dispatch::SamplerInterface for CoreSampler {}
impl Drop for CoreSampler {

View File

@ -157,6 +157,11 @@ pub trait DeviceInterface: CommonTraits {
) -> DispatchPipelineCache;
fn create_buffer(&self, desc: &crate::BufferDescriptor<'_>) -> DispatchBuffer;
fn create_texture(&self, desc: &crate::TextureDescriptor<'_>) -> DispatchTexture;
fn create_external_texture(
&self,
desc: &crate::ExternalTextureDescriptor<'_>,
planes: &[&crate::TextureView],
) -> DispatchExternalTexture;
fn create_blas(
&self,
desc: &crate::CreateBlasDescriptor<'_>,
@ -257,6 +262,9 @@ pub trait TextureInterface: CommonTraits {
fn destroy(&self);
}
pub trait ExternalTextureInterface: CommonTraits {
fn destroy(&self);
}
pub trait BlasInterface: CommonTraits {
fn prepare_compact_async(&self, callback: BlasCompactCallback);
fn ready_for_compaction(&self) -> bool;
@ -840,6 +848,7 @@ dispatch_types! {ref type DispatchTextureView: TextureViewInterface = CoreTextur
dispatch_types! {ref type DispatchSampler: SamplerInterface = CoreSampler, WebSampler, DynSampler}
dispatch_types! {ref type DispatchBuffer: BufferInterface = CoreBuffer, WebBuffer, DynBuffer}
dispatch_types! {ref type DispatchTexture: TextureInterface = CoreTexture, WebTexture, DynTexture}
dispatch_types! {ref type DispatchExternalTexture: ExternalTextureInterface = CoreExternalTexture, WebExternalTexture, DynExternalTexture}
dispatch_types! {ref type DispatchBlas: BlasInterface = CoreBlas, WebBlas, DynBlas}
dispatch_types! {ref type DispatchTlas: TlasInterface = CoreTlas, WebTlas, DynTlas}
dispatch_types! {ref type DispatchQuerySet: QuerySetInterface = CoreQuerySet, WebQuerySet, DynQuerySet}

View File

@ -91,20 +91,20 @@ pub use wgt::{
CommandBufferDescriptor, CompareFunction, CompositeAlphaMode, CopyExternalImageDestInfo,
CoreCounters, DepthBiasState, DepthStencilState, DeviceLostReason, DeviceType,
DownlevelCapabilities, DownlevelFlags, DownlevelLimits, Dx12BackendOptions, Dx12Compiler,
DxcShaderModel, DynamicOffset, Extent3d, Face, Features, FeaturesWGPU, FeaturesWebGPU,
FilterMode, FrontFace, GlBackendOptions, GlFenceBehavior, Gles3MinorVersion, HalCounters,
ImageSubresourceRange, IndexFormat, InstanceDescriptor, InstanceFlags, InternalCounters,
Limits, MemoryBudgetThresholds, MemoryHints, MultisampleState, NoopBackendOptions, Origin2d,
Origin3d, PipelineStatisticsTypes, PollError, PollStatus, PolygonMode, PowerPreference,
PredefinedColorSpace, PresentMode, PresentationTimestamp, PrimitiveState, PrimitiveTopology,
PushConstantRange, QueryType, RenderBundleDepthStencil, RequestAdapterError,
SamplerBindingType, SamplerBorderColor, ShaderLocation, ShaderModel, ShaderRuntimeChecks,
ShaderStages, StencilFaceState, StencilOperation, StencilState, StorageTextureAccess,
SurfaceCapabilities, SurfaceStatus, TexelCopyBufferLayout, TextureAspect, TextureDimension,
TextureFormat, TextureFormatFeatureFlags, TextureFormatFeatures, TextureSampleType,
TextureTransition, TextureUsages, TextureUses, TextureViewDimension, Trace, VertexAttribute,
VertexFormat, VertexStepMode, WasmNotSend, WasmNotSendSync, WasmNotSync, COPY_BUFFER_ALIGNMENT,
COPY_BYTES_PER_ROW_ALIGNMENT, MAP_ALIGNMENT, PUSH_CONSTANT_ALIGNMENT,
DxcShaderModel, DynamicOffset, Extent3d, ExternalTextureFormat, Face, Features, FeaturesWGPU,
FeaturesWebGPU, FilterMode, FrontFace, GlBackendOptions, GlFenceBehavior, Gles3MinorVersion,
HalCounters, ImageSubresourceRange, IndexFormat, InstanceDescriptor, InstanceFlags,
InternalCounters, Limits, MemoryBudgetThresholds, MemoryHints, MultisampleState,
NoopBackendOptions, Origin2d, Origin3d, PipelineStatisticsTypes, PollError, PollStatus,
PolygonMode, PowerPreference, PredefinedColorSpace, PresentMode, PresentationTimestamp,
PrimitiveState, PrimitiveTopology, PushConstantRange, QueryType, RenderBundleDepthStencil,
RequestAdapterError, SamplerBindingType, SamplerBorderColor, ShaderLocation, ShaderModel,
ShaderRuntimeChecks, ShaderStages, StencilFaceState, StencilOperation, StencilState,
StorageTextureAccess, SurfaceCapabilities, SurfaceStatus, TexelCopyBufferLayout, TextureAspect,
TextureDimension, TextureFormat, TextureFormatFeatureFlags, TextureFormatFeatures,
TextureSampleType, TextureTransition, TextureUsages, TextureUses, TextureViewDimension, Trace,
VertexAttribute, VertexFormat, VertexStepMode, WasmNotSend, WasmNotSendSync, WasmNotSync,
COPY_BUFFER_ALIGNMENT, COPY_BYTES_PER_ROW_ALIGNMENT, MAP_ALIGNMENT, PUSH_CONSTANT_ALIGNMENT,
QUERY_RESOLVE_BUFFER_ALIGNMENT, QUERY_SET_MAX_QUERIES, QUERY_SIZE, VERTEX_ALIGNMENT,
};