1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 use std::{num::NonZeroU32, ops::Range}; 6 7 #[cfg(feature = "trace")] 8 use crate::device::trace::Command as TraceCommand; 9 use crate::{ 10 command::CommandBuffer, 11 conv, 12 device::all_buffer_stages, 13 hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Token}, 14 id::{BufferId, CommandEncoderId, TextureId}, 15 memory_init_tracker::{MemoryInitKind, MemoryInitTrackerAction}, 16 resource::{BufferUse, TextureUse}, 17 track::TextureSelector, 18 }; 19 20 use hal::command::CommandBuffer as _; 21 use thiserror::Error; 22 use wgt::{ 23 BufferAddress, BufferSize, BufferUsage, ImageSubresourceRange, TextureAspect, TextureUsage, 24 }; 25 26 /// Error encountered while attempting a clear. 27 #[derive(Clone, Debug, Error)] 28 pub enum ClearError { 29 #[error("to use clear_buffer/texture the CLEAR_COMMANDS feature needs to be enabled")] 30 MissingClearCommandsFeature, 31 #[error("command encoder {0:?} is invalid")] 32 InvalidCommandEncoder(CommandEncoderId), 33 #[error("buffer {0:?} is invalid or destroyed")] 34 InvalidBuffer(BufferId), 35 #[error("texture {0:?} is invalid or destroyed")] 36 InvalidTexture(TextureId), 37 #[error("buffer clear size {0:?} is not a multiple of `COPY_BUFFER_ALIGNMENT`")] 38 UnalignedFillSize(BufferSize), 39 #[error("buffer offset {0:?} is not a multiple of `COPY_BUFFER_ALIGNMENT`")] 40 UnalignedBufferOffset(BufferAddress), 41 #[error("clear of {start_offset}..{end_offset} would end up overrunning the bounds of the buffer of size {buffer_size}")] 42 BufferOverrun { 43 start_offset: BufferAddress, 44 end_offset: BufferAddress, 45 buffer_size: BufferAddress, 46 }, 47 #[error("destination buffer/texture is missing the `COPY_DST` usage flag")] 48 MissingCopyDstUsageFlag(Option<BufferId>, Option<TextureId>), 49 #[error("texture lacks the aspects that were specified in the image subresource range. Texture has {texture_aspects:?}, specified was {subresource_range_aspects:?}")] 50 MissingTextureAspect { 51 texture_aspects: hal::format::Aspects, 52 subresource_range_aspects: TextureAspect, 53 }, 54 #[error("image subresource level range is outside of the texture's level range. texture range is {texture_level_range:?}, \ 55 whereas subesource range specified start {subresource_base_mip_level} and count {subresource_mip_level_count:?}")] 56 InvalidTextureLevelRange { 57 texture_level_range: Range<hal::image::Level>, 58 subresource_base_mip_level: u32, 59 subresource_mip_level_count: Option<NonZeroU32>, 60 }, 61 #[error("image subresource layer range is outside of the texture's layer range. texture range is {texture_layer_range:?}, \ 62 whereas subesource range specified start {subresource_base_array_layer} and count {subresource_array_layer_count:?}")] 63 InvalidTextureLayerRange { 64 texture_layer_range: Range<hal::image::Layer>, 65 subresource_base_array_layer: u32, 66 subresource_array_layer_count: Option<NonZeroU32>, 67 }, 68 } 69 70 impl<G: GlobalIdentityHandlerFactory> Global<G> { command_encoder_clear_buffer<B: GfxBackend>( &self, command_encoder_id: CommandEncoderId, dst: BufferId, offset: BufferAddress, size: Option<BufferSize>, ) -> Result<(), ClearError>71 pub fn command_encoder_clear_buffer<B: GfxBackend>( 72 &self, 73 command_encoder_id: CommandEncoderId, 74 dst: BufferId, 75 offset: BufferAddress, 76 size: Option<BufferSize>, 77 ) -> Result<(), ClearError> { 78 profiling::scope!("CommandEncoder::fill_buffer"); 79 80 let hub = B::hub(self); 81 let mut token = Token::root(); 82 let (mut cmd_buf_guard, mut token) = hub.command_buffers.write(&mut token); 83 let cmd_buf = CommandBuffer::get_encoder_mut(&mut *cmd_buf_guard, command_encoder_id) 84 .map_err(|_| ClearError::InvalidCommandEncoder(command_encoder_id))?; 85 let (buffer_guard, _) = hub.buffers.read(&mut token); 86 87 #[cfg(feature = "trace")] 88 if let Some(ref mut list) = cmd_buf.commands { 89 list.push(TraceCommand::ClearBuffer { dst, offset, size }); 90 } 91 92 if !cmd_buf.support_fill_buffer_texture { 93 return Err(ClearError::MissingClearCommandsFeature); 94 } 95 96 let (dst_buffer, dst_pending) = cmd_buf 97 .trackers 98 .buffers 99 .use_replace(&*buffer_guard, dst, (), BufferUse::COPY_DST) 100 .map_err(ClearError::InvalidBuffer)?; 101 let &(ref dst_raw, _) = dst_buffer 102 .raw 103 .as_ref() 104 .ok_or(ClearError::InvalidBuffer(dst))?; 105 if !dst_buffer.usage.contains(BufferUsage::COPY_DST) { 106 return Err(ClearError::MissingCopyDstUsageFlag(Some(dst), None)); 107 } 108 109 // Check if offset & size are valid. 110 if offset % wgt::COPY_BUFFER_ALIGNMENT != 0 { 111 return Err(ClearError::UnalignedBufferOffset(offset)); 112 } 113 if let Some(size) = size { 114 if size.get() % wgt::COPY_BUFFER_ALIGNMENT != 0 { 115 return Err(ClearError::UnalignedFillSize(size)); 116 } 117 let destination_end_offset = offset + size.get(); 118 if destination_end_offset > dst_buffer.size { 119 return Err(ClearError::BufferOverrun { 120 start_offset: offset, 121 end_offset: destination_end_offset, 122 buffer_size: dst_buffer.size, 123 }); 124 } 125 } 126 127 let num_bytes_filled = size.map_or(dst_buffer.size - offset, |s| s.get()); 128 if num_bytes_filled == 0 { 129 log::trace!("Ignoring fill_buffer of size 0"); 130 return Ok(()); 131 } 132 133 // Mark dest as initialized. 134 cmd_buf.buffer_memory_init_actions.extend( 135 dst_buffer 136 .initialization_status 137 .check(offset..(offset + num_bytes_filled)) 138 .map(|range| MemoryInitTrackerAction { 139 id: dst, 140 range, 141 kind: MemoryInitKind::ImplicitlyInitialized, 142 }), 143 ); 144 145 // actual hal barrier & operation 146 let dst_barrier = dst_pending 147 .map(|pending| pending.into_hal(dst_buffer)) 148 .next(); 149 let cmd_buf_raw = cmd_buf.raw.last_mut().unwrap(); 150 unsafe { 151 cmd_buf_raw.pipeline_barrier( 152 all_buffer_stages()..hal::pso::PipelineStage::TRANSFER, 153 hal::memory::Dependencies::empty(), 154 dst_barrier.into_iter(), 155 ); 156 cmd_buf_raw.fill_buffer( 157 dst_raw, 158 hal::buffer::SubRange { 159 offset, 160 size: size.map(|s| s.get()), 161 }, 162 0, 163 ); 164 } 165 Ok(()) 166 } 167 command_encoder_clear_image<B: GfxBackend>( &self, command_encoder_id: CommandEncoderId, dst: TextureId, subresource_range: ImageSubresourceRange, ) -> Result<(), ClearError>168 pub fn command_encoder_clear_image<B: GfxBackend>( 169 &self, 170 command_encoder_id: CommandEncoderId, 171 dst: TextureId, 172 subresource_range: ImageSubresourceRange, 173 ) -> Result<(), ClearError> { 174 profiling::scope!("CommandEncoder::clear_image"); 175 176 let hub = B::hub(self); 177 let mut token = Token::root(); 178 let (mut cmd_buf_guard, mut token) = hub.command_buffers.write(&mut token); 179 let cmd_buf = CommandBuffer::get_encoder_mut(&mut *cmd_buf_guard, command_encoder_id) 180 .map_err(|_| ClearError::InvalidCommandEncoder(command_encoder_id))?; 181 let (_, mut token) = hub.buffers.read(&mut token); // skip token 182 let (texture_guard, _) = hub.textures.read(&mut token); 183 184 #[cfg(feature = "trace")] 185 if let Some(ref mut list) = cmd_buf.commands { 186 list.push(TraceCommand::ClearImage { 187 dst, 188 subresource_range: subresource_range.clone(), 189 }); 190 } 191 192 if !cmd_buf.support_fill_buffer_texture { 193 return Err(ClearError::MissingClearCommandsFeature); 194 } 195 196 let dst_texture = texture_guard 197 .get(dst) 198 .map_err(|_| ClearError::InvalidTexture(dst))?; 199 200 // Check if subresource aspects are valid. 201 let aspects = match subresource_range.aspect { 202 wgt::TextureAspect::All => dst_texture.aspects, 203 wgt::TextureAspect::DepthOnly => hal::format::Aspects::DEPTH, 204 wgt::TextureAspect::StencilOnly => hal::format::Aspects::STENCIL, 205 }; 206 if !dst_texture.aspects.contains(aspects) { 207 return Err(ClearError::MissingTextureAspect { 208 texture_aspects: dst_texture.aspects, 209 subresource_range_aspects: subresource_range.aspect, 210 }); 211 }; 212 // Check if subresource level range is valid 213 let subresource_level_end = if let Some(count) = subresource_range.mip_level_count { 214 (subresource_range.base_mip_level + count.get()) as u8 215 } else { 216 dst_texture.full_range.levels.end 217 }; 218 if dst_texture.full_range.levels.start > subresource_range.base_mip_level as u8 219 || dst_texture.full_range.levels.end < subresource_level_end 220 { 221 return Err(ClearError::InvalidTextureLevelRange { 222 texture_level_range: dst_texture.full_range.levels.clone(), 223 subresource_base_mip_level: subresource_range.base_mip_level, 224 subresource_mip_level_count: subresource_range.mip_level_count, 225 }); 226 } 227 // Check if subresource layer range is valid 228 let subresource_layer_end = if let Some(count) = subresource_range.array_layer_count { 229 (subresource_range.base_array_layer + count.get()) as u16 230 } else { 231 dst_texture.full_range.layers.end 232 }; 233 if dst_texture.full_range.layers.start > subresource_range.base_array_layer as u16 234 || dst_texture.full_range.layers.end < subresource_layer_end 235 { 236 return Err(ClearError::InvalidTextureLayerRange { 237 texture_layer_range: dst_texture.full_range.layers.clone(), 238 subresource_base_array_layer: subresource_range.base_array_layer, 239 subresource_array_layer_count: subresource_range.array_layer_count, 240 }); 241 } 242 243 // query from tracker with usage (and check usage) 244 let (dst_texture, dst_pending) = cmd_buf 245 .trackers 246 .textures 247 .use_replace( 248 &*texture_guard, 249 dst, 250 TextureSelector { 251 levels: subresource_range.base_mip_level as u8..subresource_level_end, 252 layers: subresource_range.base_array_layer as u16..subresource_layer_end, 253 }, 254 TextureUse::COPY_DST, 255 ) 256 .map_err(ClearError::InvalidTexture)?; 257 let &(ref dst_raw, _) = dst_texture 258 .raw 259 .as_ref() 260 .ok_or(ClearError::InvalidTexture(dst))?; 261 if !dst_texture.usage.contains(TextureUsage::COPY_DST) { 262 return Err(ClearError::MissingCopyDstUsageFlag(None, Some(dst))); 263 } 264 265 // actual hal barrier & operation 266 let dst_barrier = dst_pending 267 .map(|pending| pending.into_hal(dst_texture)) 268 .next(); 269 let cmd_buf_raw = cmd_buf.raw.last_mut().unwrap(); 270 unsafe { 271 cmd_buf_raw.pipeline_barrier( 272 all_buffer_stages()..hal::pso::PipelineStage::TRANSFER, 273 hal::memory::Dependencies::empty(), 274 dst_barrier.into_iter(), 275 ); 276 cmd_buf_raw.clear_image( 277 dst_raw, 278 hal::image::Layout::TransferDstOptimal, 279 hal::command::ClearValue { 280 color: hal::command::ClearColor { 281 float32: conv::map_color_f32(&wgt::Color::TRANSPARENT), 282 }, 283 }, 284 std::iter::once(hal::image::SubresourceRange { 285 aspects, 286 level_start: subresource_range.base_mip_level as u8, 287 level_count: subresource_range.mip_level_count.map(|c| c.get() as u8), 288 layer_start: subresource_range.base_array_layer as u16, 289 layer_count: subresource_range.array_layer_count.map(|c| c.get() as u16), 290 }), 291 ); 292 } 293 Ok(()) 294 } 295 } 296