rafx_framework/upload/
image_upload.rs

1use crate::upload::gpu_image_data::GpuImageData;
2use rafx_api::extra::upload::{RafxTransferUpload, RafxUploadError};
3use rafx_api::{
4    RafxBarrierQueueTransition, RafxCmdCopyBufferToTextureParams, RafxDeviceContext, RafxExtents3D,
5    RafxQueue, RafxResourceState, RafxResourceType, RafxSampleCount, RafxTexture,
6    RafxTextureBarrier, RafxTextureDef, RafxTextureDimensions,
7};
8
9pub struct ImageUploadParams<'a> {
10    pub resource_type: RafxResourceType,
11    pub generate_mips: bool,
12    pub layer_swizzle: Option<&'a [u32]>,
13}
14
15impl<'a> Default for ImageUploadParams<'a> {
16    fn default() -> Self {
17        ImageUploadParams {
18            resource_type: RafxResourceType::TEXTURE,
19            generate_mips: false,
20            layer_swizzle: None,
21        }
22    }
23}
24
25// This function is a little more complex to use than enqueue_load_images but can support cubemaps
26// We create a layer for each layer_image_assignment, and copy from the decoded_image
27// at the index matching the assignment
28pub fn enqueue_load_image(
29    device_context: &RafxDeviceContext,
30    upload: &mut RafxTransferUpload,
31    image_data: &GpuImageData,
32    params: ImageUploadParams,
33) -> Result<RafxTexture, RafxUploadError> {
34    // All images must have identical mip level count, sizes, etc.
35    #[cfg(debug_assertions)]
36    image_data.verify_state();
37
38    //
39    // Determine the total amount of data we need to upload and verify there is enough space
40    //
41    let bytes_required = image_data.total_size(
42        device_context.device_info().upload_texture_alignment,
43        device_context.device_info().upload_texture_row_alignment,
44    );
45
46    let has_space_available = upload.has_space_available(
47        bytes_required as usize,
48        device_context.device_info().upload_texture_alignment as usize,
49        1,
50    );
51
52    if !has_space_available {
53        Err(RafxUploadError::BufferFull)?;
54    }
55
56    //
57    // Determine mip count
58    //
59    let mip_count = if params.generate_mips {
60        rafx_api::extra::mipmaps::mip_level_max_count_for_image_size(
61            image_data.width,
62            image_data.height,
63        )
64    } else {
65        image_data.layers[0].mip_levels.len() as u32
66    };
67
68    //
69    // Push all image layers/levels into the staging buffer, keeping note of offsets within the
70    // buffer where each resource is stored
71    //
72    let mut layer_offsets = Vec::default();
73    for layer in &image_data.layers {
74        let mut level_offsets = Vec::default();
75        for level in &layer.mip_levels {
76            let level_block_width = rafx_base::memory::round_size_up_to_alignment_u32(
77                level.width,
78                image_data.format.block_width_in_pixels(),
79            ) / image_data.format.block_width_in_pixels();
80            let level_block_height = rafx_base::memory::round_size_up_to_alignment_u32(
81                level.height,
82                image_data.format.block_height_in_pixels(),
83            ) / image_data.format.block_height_in_pixels();
84
85            // A block format's row may be multiple pixels high
86            let row_size_in_bytes =
87                level_block_width * image_data.format.block_or_pixel_size_in_bytes();
88            for row_index in 0..level_block_height {
89                let alignment = if row_index == 0 {
90                    device_context.device_info().upload_texture_alignment
91                } else {
92                    device_context.device_info().upload_texture_row_alignment
93                };
94
95                let offset = upload.push(
96                    &level.data[((row_size_in_bytes * row_index) as usize)
97                        ..((row_size_in_bytes * (row_index + 1)) as usize)],
98                    alignment as usize,
99                )?;
100
101                if row_index == 0 {
102                    level_offsets.push(offset);
103                }
104            }
105        }
106        layer_offsets.push(level_offsets);
107    }
108
109    // If we are swizzling layers, we create a layer per layer_swizzle entry. Otherwise, we use
110    // the number of layers in the image data
111    let layer_count = params
112        .layer_swizzle
113        .map(|x| x.len())
114        .unwrap_or_else(|| image_data.layers.len()) as u32;
115
116    //
117    // Create the texture
118    //
119    assert!(mip_count > 0);
120    let texture = device_context.create_texture(&RafxTextureDef {
121        extents: RafxExtents3D {
122            width: image_data.width,
123            height: image_data.height,
124            depth: 1,
125        },
126        array_length: layer_count,
127        mip_count,
128        sample_count: RafxSampleCount::SampleCount1,
129        format: image_data.format,
130        resource_type: params.resource_type,
131        dimensions: RafxTextureDimensions::Dim2D,
132    })?;
133
134    //
135    // Write into the transfer command buffer
136    // - transition destination memory to receive the data
137    // - copy the data
138    // - transition the destination to the graphics queue
139    //
140
141    upload
142        .transfer_command_buffer()
143        .cmd_resource_barrier(
144            &[],
145            &[RafxTextureBarrier {
146                texture: &texture,
147                src_state: RafxResourceState::UNDEFINED,
148                dst_state: RafxResourceState::COPY_DST,
149                queue_transition: RafxBarrierQueueTransition::None,
150                array_slice: None,
151                mip_slice: None,
152            }],
153        )
154        .unwrap();
155
156    for dst_layer_index in 0..layer_count {
157        let src_layer_index = if let Some(layer_swizzle) = params.layer_swizzle {
158            layer_swizzle[dst_layer_index as usize] as usize
159        } else {
160            dst_layer_index as usize
161        };
162
163        for level_index in 0..image_data.layers[src_layer_index].mip_levels.len() {
164            upload
165                .transfer_command_buffer()
166                .cmd_copy_buffer_to_texture(
167                    upload.staging_buffer(),
168                    &texture,
169                    &RafxCmdCopyBufferToTextureParams {
170                        buffer_offset: layer_offsets[src_layer_index][level_index],
171                        array_layer: dst_layer_index as u16,
172                        mip_level: level_index as u8,
173                    },
174                )
175                .unwrap();
176        }
177    }
178
179    log::debug!(
180        "upload image {}x{} format {:?} layers: {} levels: {} generate mips: {} resource type: {:?}",
181        image_data.width,
182        image_data.height,
183        image_data.format,
184        layer_count,
185        mip_count,
186        params.generate_mips,
187        params.resource_type
188    );
189
190    if params.generate_mips && mip_count > 1 {
191        //DX12TODO: Not supported yet
192        assert!(!device_context.is_dx12());
193
194        //
195        // Transition the first mip range to COPY_SRC on graphics queue (release)
196        //
197        upload.transfer_command_buffer().cmd_resource_barrier(
198            &[],
199            &[RafxTextureBarrier {
200                texture: &texture,
201                src_state: RafxResourceState::COPY_DST,
202                dst_state: RafxResourceState::COPY_SRC,
203                queue_transition: RafxBarrierQueueTransition::ReleaseTo(
204                    upload.dst_queue().queue_type(),
205                ),
206                array_slice: None,
207                mip_slice: Some(0),
208            }],
209        )?;
210
211        //
212        // Transition the first mip range to COPY_SRC on graphics queue (acquire)
213        //
214        upload.dst_command_buffer().cmd_resource_barrier(
215            &[],
216            &[RafxTextureBarrier {
217                texture: &texture,
218                src_state: RafxResourceState::COPY_DST,
219                dst_state: RafxResourceState::COPY_SRC,
220                queue_transition: RafxBarrierQueueTransition::AcquireFrom(
221                    upload.transfer_queue().queue_type(),
222                ),
223                array_slice: None,
224                mip_slice: Some(0),
225            }],
226        )?;
227
228        rafx_api::extra::mipmaps::generate_mipmaps(upload.dst_command_buffer(), &texture)?;
229
230        //
231        // Transition everything to the final layout
232        //
233        upload.dst_command_buffer().cmd_resource_barrier(
234            &[],
235            &[RafxTextureBarrier {
236                texture: &texture,
237                src_state: RafxResourceState::COPY_SRC,
238                dst_state: RafxResourceState::SHADER_RESOURCE,
239                queue_transition: RafxBarrierQueueTransition::None,
240                array_slice: None,
241                mip_slice: None,
242            }],
243        )?;
244    } else {
245        upload.transfer_command_buffer().cmd_resource_barrier(
246            &[],
247            &[RafxTextureBarrier {
248                texture: &texture,
249                src_state: RafxResourceState::COPY_DST,
250                dst_state: RafxResourceState::SHADER_RESOURCE,
251                queue_transition: RafxBarrierQueueTransition::ReleaseTo(
252                    upload.dst_queue().queue_type(),
253                ),
254                array_slice: None,
255                mip_slice: None,
256            }],
257        )?;
258
259        upload.dst_command_buffer().cmd_resource_barrier(
260            &[],
261            &[RafxTextureBarrier {
262                texture: &texture,
263                src_state: RafxResourceState::COPY_DST,
264                dst_state: RafxResourceState::SHADER_RESOURCE,
265                queue_transition: RafxBarrierQueueTransition::AcquireFrom(
266                    upload.transfer_queue().queue_type(),
267                ),
268                array_slice: None,
269                mip_slice: None,
270            }],
271        )?;
272    }
273
274    Ok(texture)
275}
276
277pub fn load_image_blocking(
278    device_context: &RafxDeviceContext,
279    transfer_queue: &RafxQueue,
280    dst_queue: &RafxQueue,
281    upload_buffer_max_size: u64,
282    image_data: &GpuImageData,
283    params: ImageUploadParams,
284) -> Result<RafxTexture, RafxUploadError> {
285    let total_size = image_data.total_size(
286        device_context.device_info().upload_texture_alignment,
287        device_context.device_info().upload_texture_row_alignment,
288    );
289    if upload_buffer_max_size < total_size {
290        Err(RafxUploadError::BufferFull)?;
291    }
292
293    let mut upload = RafxTransferUpload::new(
294        device_context,
295        transfer_queue,
296        dst_queue,
297        upload_buffer_max_size,
298        None,
299    )?;
300
301    let texture = enqueue_load_image(device_context, &mut upload, image_data, params)?;
302
303    upload.block_until_upload_complete()?;
304
305    Ok(texture)
306}