diff --git a/tests/tests/wgpu-gpu/render_target.rs b/tests/tests/wgpu-gpu/render_target.rs index 6a89fb1756..27a935e309 100644 --- a/tests/tests/wgpu-gpu/render_target.rs +++ b/tests/tests/wgpu-gpu/render_target.rs @@ -2,7 +2,7 @@ use wgpu::{ util::{BufferInitDescriptor, DeviceExt}, vertex_attr_array, }; -use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters, TestingContext}; +use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters, TestingContext}; #[gpu_test] static DRAW_TO_2D_VIEW: GpuTestConfiguration = GpuTestConfiguration::new() @@ -233,3 +233,188 @@ async fn run_test( let succeeded = data.iter().all(|b| *b == u8::MAX); assert!(succeeded); } + +#[gpu_test] +static DRAW_TO_3D_VIEW: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( + TestParameters::default() + .limits(wgpu::Limits { + max_texture_dimension_3d: 512, + ..wgpu::Limits::downlevel_webgl2_defaults() + }) + .skip(FailureCase::backend(wgpu::Backends::VULKAN)), + ) + .run_async(run_test_3d); + +async fn run_test_3d(ctx: TestingContext) { + let vertex_buffer_content: &[f32; 12] = &[ + // Triangle 1 + -1.0, -1.0, // Bottom left + 1.0, 1.0, // Top right + -1.0, 1.0, // Top left + // Triangle 2 + -1.0, -1.0, // Bottom left + 1.0, -1.0, // Bottom right + 1.0, 1.0, // Top right + ]; + let vertex_buffer = ctx.device.create_buffer_init(&BufferInitDescriptor { + label: None, + contents: bytemuck::cast_slice(vertex_buffer_content), + usage: wgpu::BufferUsages::VERTEX, + }); + + let shader_src = " + @vertex + fn vs_main(@location(0) position: vec2f) -> @builtin(position) vec4f { + return vec4f(position, 0.0, 1.0); + } + + @fragment + fn fs_main() -> @location(0) vec4f { + return vec4f(1.0); + } + "; + + let shader = ctx + .device + .create_shader_module(wgpu::ShaderModuleDescriptor { + label: None, + source: wgpu::ShaderSource::Wgsl(shader_src.into()), + }); + + let pipeline_desc = wgpu::RenderPipelineDescriptor { + label: None, + layout: None, + vertex: wgpu::VertexState { + buffers: &[wgpu::VertexBufferLayout { + array_stride: 8, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &vertex_attr_array![0 => Float32x2], + }], + module: &shader, + entry_point: Some("vs_main"), + compilation_options: Default::default(), + }, + primitive: wgpu::PrimitiveState::default(), + depth_stencil: None, + multisample: wgpu::MultisampleState::default(), + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: Some("fs_main"), + compilation_options: Default::default(), + targets: &[Some(wgpu::ColorTargetState { + format: wgpu::TextureFormat::R8Unorm, + blend: None, + write_mask: wgpu::ColorWrites::ALL, + })], + }), + multiview: None, + cache: None, + }; + let pipeline = ctx.device.create_render_pipeline(&pipeline_desc); + + const SIZE: u32 = 512; + const DEPTH: u32 = 2; + const MIPS: u32 = 2; + const fn size_for_mips(mips: u32) -> u64 { + let mut out: u64 = 0; + let mut mip = 0; + while mip < mips { + let size = SIZE as u64 >> mip; + let z = DEPTH as u64 >> mip; + out += size * size * z; + + mip += 1; + } + out + } + + let out_texture = ctx.device.create_texture(&wgpu::TextureDescriptor { + label: None, + size: wgpu::Extent3d { + width: SIZE, + height: SIZE, + depth_or_array_layers: DEPTH, + }, + mip_level_count: MIPS, + sample_count: 1, + dimension: wgpu::TextureDimension::D3, + format: wgpu::TextureFormat::R8Unorm, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC, + view_formats: &[], + }); + + let readback_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: size_for_mips(MIPS), + usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ, + mapped_at_creation: false, + }); + + let mut encoder = ctx + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); + + for mip in 0..MIPS { + let out_texture_view = out_texture.create_view(&wgpu::TextureViewDescriptor { + base_mip_level: mip, + mip_level_count: Some(1), + ..Default::default() + }); + for layer in 0..DEPTH >> mip { + let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: None, + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &out_texture_view, + depth_slice: Some(layer), + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color::BLACK), + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: None, + timestamp_writes: None, + occlusion_query_set: None, + }); + rpass.set_pipeline(&pipeline); + rpass.set_vertex_buffer(0, vertex_buffer.slice(..)); + rpass.draw(0..6, 0..1); + } + } + + for mip in 0..MIPS { + encoder.copy_texture_to_buffer( + wgpu::TexelCopyTextureInfo { + texture: &out_texture, + mip_level: mip, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + wgpu::TexelCopyBufferInfo { + buffer: &readback_buffer, + layout: wgpu::TexelCopyBufferLayout { + offset: size_for_mips(mip), + bytes_per_row: Some(SIZE >> mip), + rows_per_image: Some(SIZE >> mip), + }, + }, + wgpu::Extent3d { + width: SIZE >> mip, + height: SIZE >> mip, + depth_or_array_layers: DEPTH >> mip, + }, + ); + } + + ctx.queue.submit([encoder.finish()]); + + let slice = readback_buffer.slice(..); + slice.map_async(wgpu::MapMode::Read, |_| ()); + + ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + + let data = slice.get_mapped_range(); + let succeeded = data.iter().all(|b| *b == u8::MAX); + assert!(succeeded); +} diff --git a/wgpu-core/src/command/render.rs b/wgpu-core/src/command/render.rs index 1f502d86d5..751a19683e 100644 --- a/wgpu-core/src/command/render.rs +++ b/wgpu-core/src/command/render.rs @@ -14,7 +14,7 @@ use crate::command::{ }; use crate::init_tracker::BufferInitTrackerAction; use crate::pipeline::{RenderPipeline, VertexStep}; -use crate::resource::InvalidResourceError; +use crate::resource::{InvalidResourceError, ResourceErrorIdent}; use crate::snatch::SnatchGuard; use crate::{ api_log, @@ -621,6 +621,18 @@ pub enum ColorAttachmentError { TooMany { given: usize, limit: usize }, #[error("The total number of bytes per sample in color attachments {total} exceeds the limit {limit}")] TooManyBytesPerSample { total: u32, limit: u32 }, + #[error("Depth slice must be less than {limit} but is {given}")] + DepthSliceLimit { given: u32, limit: u32 }, + #[error("Color attachment's view is 3D and requires depth slice to be provided")] + MissingDepthSlice, + #[error("Depth slice was provided but the color attachment's view is not 3D")] + UnneededDepthSlice, + #[error("{view}'s subresource at mip {mip_level} and depth/array layer {depth_or_array_layer} is already attached to this render pass")] + SubresourceOverlap { + view: ResourceErrorIdent, + mip_level: u32, + depth_or_array_layer: u32, + }, } #[derive(Clone, Debug, Error)] @@ -1096,6 +1108,8 @@ impl<'d> RenderPassInfo<'d> { }); } + let mut attachment_set = crate::FastHashSet::default(); + let mut color_attachments_hal = ArrayVec::>, { hal::MAX_COLOR_ATTACHMENTS }>::new(); for (index, attachment) in color_attachments.iter().enumerate() { @@ -1126,6 +1140,71 @@ impl<'d> RenderPassInfo<'d> { )); } + if color_view.desc.dimension == TextureViewDimension::D3 { + if let Some(depth_slice) = at.depth_slice { + let mip = color_view.desc.range.base_mip_level; + let mip_size = color_view + .parent + .desc + .size + .mip_level_size(mip, color_view.parent.desc.dimension); + let limit = mip_size.depth_or_array_layers; + if depth_slice >= limit { + return Err(RenderPassErrorInner::ColorAttachment( + ColorAttachmentError::DepthSliceLimit { + given: depth_slice, + limit, + }, + )); + } + } else { + return Err(RenderPassErrorInner::ColorAttachment( + ColorAttachmentError::MissingDepthSlice, + )); + } + } else if at.depth_slice.is_some() { + return Err(RenderPassErrorInner::ColorAttachment( + ColorAttachmentError::UnneededDepthSlice, + )); + } + + fn check_attachment_overlap( + attachment_set: &mut crate::FastHashSet<(crate::track::TrackerIndex, u32, u32)>, + view: &TextureView, + depth_slice: Option, + ) -> Result<(), ColorAttachmentError> { + let mut insert = |slice| { + let mip_level = view.desc.range.base_mip_level; + if attachment_set.insert((view.tracking_data.tracker_index(), mip_level, slice)) + { + Ok(()) + } else { + Err(ColorAttachmentError::SubresourceOverlap { + view: view.error_ident(), + mip_level, + depth_or_array_layer: slice, + }) + } + }; + match view.desc.dimension { + TextureViewDimension::D2 => { + insert(view.desc.range.base_array_layer)?; + } + TextureViewDimension::D2Array => { + for layer in view.selector.layers.clone() { + insert(layer)?; + } + } + TextureViewDimension::D3 => { + insert(depth_slice.unwrap())?; + } + _ => unreachable!(), + }; + Ok(()) + } + + check_attachment_overlap(&mut attachment_set, color_view, at.depth_slice)?; + Self::add_pass_texture_init_actions( at.load_op, at.store_op, @@ -1141,6 +1220,8 @@ impl<'d> RenderPassInfo<'d> { resolve_view.same_device(device)?; check_multiview(resolve_view)?; + check_attachment_overlap(&mut attachment_set, resolve_view, None)?; + let resolve_location = AttachmentErrorLocation::Color { index, resolve: true, diff --git a/wgpu-core/src/device/resource.rs b/wgpu-core/src/device/resource.rs index 9a8e5e3480..afaaef2259 100644 --- a/wgpu-core/src/device/resource.rs +++ b/wgpu-core/src/device/resource.rs @@ -929,8 +929,11 @@ impl Device { desc.format, )); } - // Renderable textures can only be 2D - if desc.usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) { + + // Renderable textures can only be 2D on Vulkan (until we implement 3D support) + if self.backend() == wgt::Backend::Vulkan + && desc.usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) + { return Err(CreateTextureError::InvalidDimensionUsages( wgt::TextureUsages::RENDER_ATTACHMENT, desc.dimension, @@ -948,6 +951,14 @@ impl Device { desc.format, )); } + + // Renderable textures can only be 2D or 3D + if desc.usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) { + return Err(CreateTextureError::InvalidDimensionUsages( + wgt::TextureUsages::RENDER_ATTACHMENT, + desc.dimension, + )); + } } if desc.format.is_compressed() { @@ -1124,17 +1135,13 @@ impl Device { let clear_mode = if hal_usage .intersects(wgt::TextureUses::DEPTH_STENCIL_WRITE | wgt::TextureUses::COLOR_TARGET) + && desc.dimension == wgt::TextureDimension::D2 { let (is_color, usage) = if desc.format.is_depth_stencil_format() { (false, wgt::TextureUses::DEPTH_STENCIL_WRITE) } else { (true, wgt::TextureUses::COLOR_TARGET) }; - let dimension = match desc.dimension { - wgt::TextureDimension::D1 => TextureViewDimension::D1, - wgt::TextureDimension::D2 => TextureViewDimension::D2, - wgt::TextureDimension::D3 => unreachable!(), - }; let clear_label = hal_label( Some("(wgpu internal) clear texture view"), @@ -1149,7 +1156,7 @@ impl Device { let desc = hal::TextureViewDescriptor { label: clear_label, format: $format, - dimension, + dimension: TextureViewDimension::D2, usage, range: wgt::ImageSubresourceRange { aspect: $aspect, @@ -1420,9 +1427,12 @@ impl Device { break 'error Err(TextureViewNotRenderableReason::Usage(resolved_usage)); } - if !(resolved_dimension == TextureViewDimension::D2 - || resolved_dimension == TextureViewDimension::D2Array) - { + let allowed_view_dimensions = [ + TextureViewDimension::D2, + TextureViewDimension::D2Array, + TextureViewDimension::D3, + ]; + if !allowed_view_dimensions.contains(&resolved_dimension) { break 'error Err(TextureViewNotRenderableReason::Dimension( resolved_dimension, )); diff --git a/wgpu-hal/src/dx12/command.rs b/wgpu-hal/src/dx12/command.rs index e7a27e069e..f57e6b9238 100644 --- a/wgpu-hal/src/dx12/command.rs +++ b/wgpu-hal/src/dx12/command.rs @@ -73,6 +73,13 @@ impl Drop for super::CommandEncoder { fn drop(&mut self) { use crate::CommandEncoder; unsafe { self.discard_encoding() } + + let mut rtv_pool = self.rtv_pool.lock(); + for handle in self.temp_rtv_handles.drain(..) { + rtv_pool.free_handle(handle); + } + drop(rtv_pool); + self.counters.command_encoders.sub(1); } } @@ -762,13 +769,39 @@ impl crate::CommandEncoder for super::CommandEncoder { let mut color_views = [Direct3D12::D3D12_CPU_DESCRIPTOR_HANDLE { ptr: 0 }; crate::MAX_COLOR_ATTACHMENTS]; + let mut rtv_pool = self.rtv_pool.lock(); for (rtv, cat) in color_views.iter_mut().zip(desc.color_attachments.iter()) { if let Some(cat) = cat.as_ref() { - *rtv = cat.target.view.handle_rtv.unwrap().raw; + if cat.target.view.dimension == wgt::TextureViewDimension::D3 { + let desc = Direct3D12::D3D12_RENDER_TARGET_VIEW_DESC { + Format: cat.target.view.raw_format, + ViewDimension: Direct3D12::D3D12_RTV_DIMENSION_TEXTURE3D, + Anonymous: Direct3D12::D3D12_RENDER_TARGET_VIEW_DESC_0 { + Texture3D: Direct3D12::D3D12_TEX3D_RTV { + MipSlice: cat.target.view.mip_slice, + FirstWSlice: cat.depth_slice.unwrap(), + WSize: 1, + }, + }, + }; + let handle = rtv_pool.alloc_handle()?; + unsafe { + self.device.CreateRenderTargetView( + &cat.target.view.texture, + Some(&desc), + handle.raw, + ) + }; + *rtv = handle.raw; + self.temp_rtv_handles.push(handle); + } else { + *rtv = cat.target.view.handle_rtv.unwrap().raw; + } } else { *rtv = self.null_rtv_handle.raw; } } + drop(rtv_pool); let ds_view = desc.depth_stencil_attachment.as_ref().map(|ds| { if ds.target.usage == wgt::TextureUses::DEPTH_STENCIL_WRITE { @@ -803,8 +836,11 @@ impl crate::CommandEncoder for super::CommandEncoder { } if let Some(ref target) = cat.resolve_target { self.pass.resolves.push(super::PassResolve { - src: cat.target.view.target_base.clone(), - dst: target.view.target_base.clone(), + src: ( + cat.target.view.texture.clone(), + cat.target.view.subresource_index, + ), + dst: (target.view.texture.clone(), target.view.subresource_index), format: target.view.raw_format, }); } diff --git a/wgpu-hal/src/dx12/device.rs b/wgpu-hal/src/dx12/device.rs index 1b48eb4466..213f64f1d5 100644 --- a/wgpu-hal/src/dx12/device.rs +++ b/wgpu-hal/src/dx12/device.rs @@ -188,7 +188,7 @@ impl super::Device { }, features, shared: Arc::new(shared), - rtv_pool: Mutex::new(rtv_pool), + rtv_pool: Arc::new(Mutex::new(rtv_pool)), dsv_pool: Mutex::new(descriptor::CpuPool::new( raw.clone(), Direct3D12::D3D12_DESCRIPTOR_HEAP_TYPE_DSV, @@ -539,10 +539,14 @@ impl crate::Device for super::Device { Ok(super::TextureView { raw_format: view_desc.rtv_dsv_format, aspects: view_desc.aspects, - target_base: ( - texture.resource.clone(), - texture.calc_subresource(desc.range.base_mip_level, desc.range.base_array_layer, 0), + dimension: desc.dimension, + texture: texture.resource.clone(), + subresource_index: texture.calc_subresource( + desc.range.base_mip_level, + desc.range.base_array_layer, + 0, ), + mip_slice: desc.range.base_mip_level, handle_srv: if desc.usage.intersects(wgt::TextureUses::RESOURCE) { match unsafe { view_desc.to_srv() } { Some(raw_desc) => { @@ -584,7 +588,10 @@ impl crate::Device for super::Device { } else { None }, - handle_rtv: if desc.usage.intersects(wgt::TextureUses::COLOR_TARGET) { + handle_rtv: if desc.usage.intersects(wgt::TextureUses::COLOR_TARGET) + && desc.dimension != wgt::TextureViewDimension::D3 + // 3D RTVs must be created in the render pass + { let raw_desc = unsafe { view_desc.to_rtv() }; let handle = self.rtv_pool.lock().alloc_handle()?; unsafe { @@ -725,6 +732,8 @@ impl crate::Device for super::Device { device: self.raw.clone(), shared: Arc::clone(&self.shared), mem_allocator: self.mem_allocator.clone(), + rtv_pool: Arc::clone(&self.rtv_pool), + temp_rtv_handles: Vec::new(), null_rtv_handle: self.null_rtv_handle, list: None, free_lists: Vec::new(), diff --git a/wgpu-hal/src/dx12/mod.rs b/wgpu-hal/src/dx12/mod.rs index 010c1ebb86..2c39b1cc59 100644 --- a/wgpu-hal/src/dx12/mod.rs +++ b/wgpu-hal/src/dx12/mod.rs @@ -646,7 +646,7 @@ pub struct Device { features: wgt::Features, shared: Arc, // CPU only pools - rtv_pool: Mutex, + rtv_pool: Arc>, dsv_pool: Mutex, srv_uav_pool: Mutex, // library @@ -798,6 +798,9 @@ pub struct CommandEncoder { shared: Arc, mem_allocator: Allocator, + rtv_pool: Arc>, + temp_rtv_handles: Vec, + null_rtv_handle: descriptor::Handle, list: Option, free_lists: Vec, @@ -918,8 +921,10 @@ impl Texture { pub struct TextureView { raw_format: Dxgi::Common::DXGI_FORMAT, aspects: crate::FormatAspects, - /// only used by resolve - target_base: (Direct3D12::ID3D12Resource, u32), + dimension: wgt::TextureViewDimension, + texture: Direct3D12::ID3D12Resource, + subresource_index: u32, + mip_slice: u32, handle_srv: Option, handle_uav: Option, handle_rtv: Option, diff --git a/wgpu-hal/src/dx12/view.rs b/wgpu-hal/src/dx12/view.rs index fa8f2a4d61..e541ac2398 100644 --- a/wgpu-hal/src/dx12/view.rs +++ b/wgpu-hal/src/dx12/view.rs @@ -250,16 +250,10 @@ impl ViewDescriptor { PlaneSlice: aspects_to_plane(self.aspects), } } - wgt::TextureViewDimension::D3 => { - desc.ViewDimension = Direct3D12::D3D12_RTV_DIMENSION_TEXTURE3D; - desc.Anonymous.Texture3D = Direct3D12::D3D12_TEX3D_RTV { - MipSlice: self.mip_level_base, - FirstWSlice: self.array_layer_base, - WSize: self.array_layer_count, - } - } - wgt::TextureViewDimension::Cube | wgt::TextureViewDimension::CubeArray => { - panic!("Unable to view texture as cube RTV") + wgt::TextureViewDimension::D3 + | wgt::TextureViewDimension::Cube + | wgt::TextureViewDimension::CubeArray => { + panic!("Unable to view texture as cube or 3D RTV") } } @@ -337,7 +331,7 @@ impl ViewDescriptor { wgt::TextureViewDimension::D3 | wgt::TextureViewDimension::Cube | wgt::TextureViewDimension::CubeArray => { - panic!("Unable to view texture as cube or 3D RTV") + panic!("Unable to view texture as cube or 3D DSV") } } diff --git a/wgpu-hal/src/gles/command.rs b/wgpu-hal/src/gles/command.rs index c0ed14ded7..6c46c55991 100644 --- a/wgpu-hal/src/gles/command.rs +++ b/wgpu-hal/src/gles/command.rs @@ -557,6 +557,7 @@ impl crate::CommandEncoder for super::CommandEncoder { self.cmd_buffer.commands.push(C::BindAttachment { attachment, view: cat.target.view.clone(), + depth_slice: cat.depth_slice, }); if let Some(ref rat) = cat.resolve_target { self.state @@ -578,6 +579,7 @@ impl crate::CommandEncoder for super::CommandEncoder { self.cmd_buffer.commands.push(C::BindAttachment { attachment, view: dsat.target.view.clone(), + depth_slice: None, }); if aspects.contains(crate::FormatAspects::DEPTH) && !dsat.depth_ops.contains(crate::AttachmentOps::STORE) diff --git a/wgpu-hal/src/gles/mod.rs b/wgpu-hal/src/gles/mod.rs index 94b169daa1..c09fc21d24 100644 --- a/wgpu-hal/src/gles/mod.rs +++ b/wgpu-hal/src/gles/mod.rs @@ -902,6 +902,7 @@ enum Command { BindAttachment { attachment: u32, view: TextureView, + depth_slice: Option, }, ResolveAttachment { attachment: u32, diff --git a/wgpu-hal/src/gles/queue.rs b/wgpu-hal/src/gles/queue.rs index 65bfe526ed..57cf07d38e 100644 --- a/wgpu-hal/src/gles/queue.rs +++ b/wgpu-hal/src/gles/queue.rs @@ -98,6 +98,7 @@ impl super::Queue { fbo_target: u32, attachment: u32, view: &super::TextureView, + depth_slice: Option, ) { match view.inner { super::TextureInner::Renderbuffer { raw } => { @@ -126,13 +127,18 @@ impl super::Queue { ) }; } else if is_layered_target(target) { + let layer = if target == glow::TEXTURE_3D { + depth_slice.unwrap() as i32 + } else { + view.array_layers.start as i32 + }; unsafe { gl.framebuffer_texture_layer( fbo_target, attachment, Some(raw), view.mip_levels.start as i32, - view.array_layers.start as i32, + layer, ) }; } else { @@ -1096,8 +1102,11 @@ impl super::Queue { C::BindAttachment { attachment, ref view, + depth_slice, } => { - unsafe { self.set_attachment(gl, glow::DRAW_FRAMEBUFFER, attachment, view) }; + unsafe { + self.set_attachment(gl, glow::DRAW_FRAMEBUFFER, attachment, view, depth_slice) + }; } C::ResolveAttachment { attachment, @@ -1108,7 +1117,13 @@ impl super::Queue { unsafe { gl.read_buffer(attachment) }; unsafe { gl.bind_framebuffer(glow::DRAW_FRAMEBUFFER, Some(self.copy_fbo)) }; unsafe { - self.set_attachment(gl, glow::DRAW_FRAMEBUFFER, glow::COLOR_ATTACHMENT0, dst) + self.set_attachment( + gl, + glow::DRAW_FRAMEBUFFER, + glow::COLOR_ATTACHMENT0, + dst, + None, + ) }; unsafe { gl.blit_framebuffer( diff --git a/wgpu-hal/src/metal/command.rs b/wgpu-hal/src/metal/command.rs index 4f534c41b0..80ef4e4e55 100644 --- a/wgpu-hal/src/metal/command.rs +++ b/wgpu-hal/src/metal/command.rs @@ -532,6 +532,9 @@ impl crate::CommandEncoder for super::CommandEncoder { if let Some(at) = at.as_ref() { let at_descriptor = descriptor.color_attachments().object_at(i as u64).unwrap(); at_descriptor.set_texture(Some(&at.target.view.raw)); + if let Some(depth_slice) = at.depth_slice { + at_descriptor.set_depth_plane(depth_slice as u64); + } if let Some(ref resolve) = at.resolve_target { //Note: the selection of levels and slices is already handled by `TextureView` at_descriptor.set_resolve_texture(Some(&resolve.view.raw));