diff --git a/wgpu-hal/src/dx12/adapter.rs b/wgpu-hal/src/dx12/adapter.rs index 22d52be4d..3a0ece692 100644 --- a/wgpu-hal/src/dx12/adapter.rs +++ b/wgpu-hal/src/dx12/adapter.rs @@ -361,7 +361,8 @@ impl super::Adapter { | wgt::Features::DUAL_SOURCE_BLENDING | wgt::Features::TEXTURE_FORMAT_NV12 | wgt::Features::FLOAT32_FILTERABLE - | wgt::Features::TEXTURE_ATOMIC; + | wgt::Features::TEXTURE_ATOMIC + | wgt::Features::EXTERNAL_TEXTURE; //TODO: in order to expose this, we need to run a compute shader // that extract the necessary statistics out of the D3D12 result. diff --git a/wgpu-hal/src/dx12/conv.rs b/wgpu-hal/src/dx12/conv.rs index 2ccb96dd8..6005f43bf 100644 --- a/wgpu-hal/src/dx12/conv.rs +++ b/wgpu-hal/src/dx12/conv.rs @@ -135,7 +135,10 @@ pub fn map_binding_type(ty: &wgt::BindingType) -> Direct3D12::D3D12_DESCRIPTOR_R } | Bt::StorageTexture { .. } => Direct3D12::D3D12_DESCRIPTOR_RANGE_TYPE_UAV, Bt::AccelerationStructure { .. } => Direct3D12::D3D12_DESCRIPTOR_RANGE_TYPE_SRV, - Bt::ExternalTexture => unimplemented!(), + // External textures require multiple bindings and therefore cannot + // be mapped to a single descriptor range type. They must be handled + // separately by the caller. + Bt::ExternalTexture => unreachable!("External textures must be handled separately"), } } diff --git a/wgpu-hal/src/dx12/device.rs b/wgpu-hal/src/dx12/device.rs index d8bdd2b1d..8eb4bb886 100644 --- a/wgpu-hal/src/dx12/device.rs +++ b/wgpu-hal/src/dx12/device.rs @@ -807,7 +807,8 @@ impl crate::Device for super::Device { | wgt::BindingType::StorageTexture { .. } | wgt::BindingType::AccelerationStructure { .. } => num_views += count, wgt::BindingType::Sampler { .. } => has_sampler_in_group = true, - wgt::BindingType::ExternalTexture => unimplemented!(), + // Three texture planes and one params buffer + wgt::BindingType::ExternalTexture => num_views += 4 * count, } } @@ -880,6 +881,7 @@ impl crate::Device for super::Device { let mut binding_map = hlsl::BindingMap::default(); let mut sampler_buffer_binding_map = hlsl::SamplerIndexBufferBindingMap::default(); + let mut external_texture_binding_map = hlsl::ExternalTextureBindingMap::default(); let mut bind_cbv = hlsl::BindTarget::default(); let mut bind_srv = hlsl::BindTarget::default(); let mut bind_uav = hlsl::BindTarget::default(); @@ -939,6 +941,8 @@ impl crate::Device for super::Device { .. } => {} wgt::BindingType::Sampler(_) => sampler_in_bind_group = true, + // Three texture planes and one params buffer + wgt::BindingType::ExternalTexture => total_non_dynamic_entries += 4, _ => total_non_dynamic_entries += 1, } } @@ -993,61 +997,110 @@ impl crate::Device for super::Device { // SRV/CBV/UAV descriptor tables let range_base = ranges.len(); for entry in bgl.entries.iter() { - let (range_ty, has_dynamic_offset) = match entry.ty { - wgt::BindingType::Buffer { - ty, - has_dynamic_offset: true, - .. - } => match ty { - wgt::BufferBindingType::Uniform => continue, - wgt::BufferBindingType::Storage { .. } => { - (conv::map_binding_type(&entry.ty), true) - } - }, - ref other => (conv::map_binding_type(other), false), - }; - let bt = match range_ty { - Direct3D12::D3D12_DESCRIPTOR_RANGE_TYPE_CBV => &mut bind_cbv, - Direct3D12::D3D12_DESCRIPTOR_RANGE_TYPE_SRV => &mut bind_srv, - Direct3D12::D3D12_DESCRIPTOR_RANGE_TYPE_UAV => &mut bind_uav, - Direct3D12::D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER => continue, - _ => todo!(), - }; - - let binding_array_size = entry.count.map(NonZeroU32::get); - - let dynamic_storage_buffer_offsets_index = if has_dynamic_offset { - debug_assert!( - binding_array_size.is_none(), - "binding arrays and dynamic buffers are mutually exclusive" + let count = entry.count.map_or(1, NonZeroU32::get); + if let wgt::BindingType::ExternalTexture = entry.ty { + // External textures need 3 SRVs (a texture for each plane) + // and 1 CBV for the parameters buffer. + let bind_target = hlsl::ExternalTextureBindTarget { + planes: core::array::from_fn(|_| hlsl::BindTarget { + register: { + let register = bind_srv.register; + bind_srv.register += count; + register + }, + ..bind_srv + }), + params: hlsl::BindTarget { + register: { + let register = bind_cbv.register; + bind_cbv.register += count; + register + }, + ..bind_cbv + }, + }; + external_texture_binding_map.insert( + naga::ResourceBinding { + group: index as u32, + binding: entry.binding, + }, + bind_target, ); - let ret = Some(dynamic_storage_buffers); - dynamic_storage_buffers += 1; - ret + for bt in bind_target.planes { + ranges.push(Direct3D12::D3D12_DESCRIPTOR_RANGE { + RangeType: Direct3D12::D3D12_DESCRIPTOR_RANGE_TYPE_SRV, + NumDescriptors: count, + BaseShaderRegister: bt.register, + RegisterSpace: bt.space as u32, + OffsetInDescriptorsFromTableStart: + Direct3D12::D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND, + }); + } + ranges.push(Direct3D12::D3D12_DESCRIPTOR_RANGE { + RangeType: Direct3D12::D3D12_DESCRIPTOR_RANGE_TYPE_CBV, + NumDescriptors: count, + BaseShaderRegister: bind_target.params.register, + RegisterSpace: bind_target.params.space as u32, + OffsetInDescriptorsFromTableStart: + Direct3D12::D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND, + }); } else { - None - }; + let (range_ty, has_dynamic_offset) = match entry.ty { + wgt::BindingType::Buffer { + ty, + has_dynamic_offset: true, + .. + } => match ty { + wgt::BufferBindingType::Uniform => continue, + wgt::BufferBindingType::Storage { .. } => { + (conv::map_binding_type(&entry.ty), true) + } + }, + ref other => (conv::map_binding_type(other), false), + }; + let bt = match range_ty { + Direct3D12::D3D12_DESCRIPTOR_RANGE_TYPE_CBV => &mut bind_cbv, + Direct3D12::D3D12_DESCRIPTOR_RANGE_TYPE_SRV => &mut bind_srv, + Direct3D12::D3D12_DESCRIPTOR_RANGE_TYPE_UAV => &mut bind_uav, + Direct3D12::D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER => continue, + _ => todo!(), + }; - binding_map.insert( - naga::ResourceBinding { - group: index as u32, - binding: entry.binding, - }, - hlsl::BindTarget { - binding_array_size, - dynamic_storage_buffer_offsets_index, - ..*bt - }, - ); - ranges.push(Direct3D12::D3D12_DESCRIPTOR_RANGE { - RangeType: range_ty, - NumDescriptors: entry.count.map_or(1, |count| count.get()), - BaseShaderRegister: bt.register, - RegisterSpace: bt.space as u32, - OffsetInDescriptorsFromTableStart: - Direct3D12::D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND, - }); - bt.register += entry.count.map(NonZeroU32::get).unwrap_or(1); + let binding_array_size = entry.count.map(NonZeroU32::get); + + let dynamic_storage_buffer_offsets_index = if has_dynamic_offset { + debug_assert!( + binding_array_size.is_none(), + "binding arrays and dynamic buffers are mutually exclusive" + ); + let ret = Some(dynamic_storage_buffers); + dynamic_storage_buffers += 1; + ret + } else { + None + }; + + binding_map.insert( + naga::ResourceBinding { + group: index as u32, + binding: entry.binding, + }, + hlsl::BindTarget { + binding_array_size, + dynamic_storage_buffer_offsets_index, + ..*bt + }, + ); + ranges.push(Direct3D12::D3D12_DESCRIPTOR_RANGE { + RangeType: range_ty, + NumDescriptors: count, + BaseShaderRegister: bt.register, + RegisterSpace: bt.space as u32, + OffsetInDescriptorsFromTableStart: + Direct3D12::D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND, + }); + bt.register += count; + } } let mut sampler_index_within_bind_group = 0; @@ -1388,7 +1441,7 @@ impl crate::Device for super::Device { restrict_indexing: true, sampler_heap_target, sampler_buffer_binding_map, - external_texture_binding_map: hlsl::ExternalTextureBindingMap::default(), + external_texture_binding_map, force_loop_bounding: true, }, }) @@ -1574,7 +1627,31 @@ impl crate::Device for super::Device { inner.stage.push(handle); } } - wgt::BindingType::ExternalTexture => unimplemented!(), + wgt::BindingType::ExternalTexture => { + // We don't yet support binding arrays of external textures. + // https://github.com/gfx-rs/wgpu/issues/8027 + assert_eq!(entry.count, 1); + let external_texture = &desc.external_textures[entry.resource_index as usize]; + for plane in &external_texture.planes { + let plane_handle = plane.view.handle_srv.unwrap(); + cpu_views.as_mut().unwrap().stage.push(plane_handle.raw); + } + let gpu_address = external_texture.params.resolve_address(); + let size = external_texture.params.resolve_size() as u32; + let inner = cpu_views.as_mut().unwrap(); + let cpu_index = inner.stage.len() as u32; + let params_handle = desc.layout.cpu_heap_views.as_ref().unwrap().at(cpu_index); + let size_mask = Direct3D12::D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT - 1; + let raw_desc = Direct3D12::D3D12_CONSTANT_BUFFER_VIEW_DESC { + BufferLocation: gpu_address, + SizeInBytes: ((size - 1) | size_mask) + 1, + }; + unsafe { + self.raw + .CreateConstantBufferView(Some(&raw_desc), params_handle) + }; + inner.stage.push(params_handle); + } } } diff --git a/wgpu-types/src/features.rs b/wgpu-types/src/features.rs index 5880c6ff3..908d5bda2 100644 --- a/wgpu-types/src/features.rs +++ b/wgpu-types/src/features.rs @@ -997,7 +997,7 @@ bitflags_array! { /// all. /// /// Supported platforms: - /// - None + /// - DX12 const EXTERNAL_TEXTURE = 1 << 30; // Shader: