hal: make copy to work on one array layer at a time

This commit is contained in:
Dzmitry Malyshau 2021-07-07 23:34:59 -04:00
parent f79c3781c3
commit 35ee65707f
14 changed files with 346 additions and 411 deletions

View File

@ -120,27 +120,34 @@ pub(crate) fn extract_texture_selector<A: hal::Api>(
});
}
let layers = match texture.desc.dimension {
wgt::TextureDimension::D1 | wgt::TextureDimension::D2 => {
copy_texture.origin.z..copy_texture.origin.z + copy_size.depth_or_array_layers
}
wgt::TextureDimension::D3 => 0..1,
let (layers, origin_z) = match texture.desc.dimension {
wgt::TextureDimension::D1 | wgt::TextureDimension::D2 => (
copy_texture.origin.z..copy_texture.origin.z + copy_size.depth_or_array_layers,
0,
),
wgt::TextureDimension::D3 => (0..1, copy_texture.origin.z),
};
let base = hal::TextureCopyBase {
origin: wgt::Origin3d {
x: copy_texture.origin.x,
y: copy_texture.origin.y,
z: origin_z,
},
// this value will be incremented per copied layer
array_layer: layers.start,
mip_level: copy_texture.mip_level,
aspect: copy_aspect,
};
let selector = TextureSelector {
levels: copy_texture.mip_level..copy_texture.mip_level + 1,
layers,
};
let base = hal::TextureCopyBase {
origin: copy_texture.origin,
mip_level: copy_texture.mip_level,
aspect: copy_aspect,
};
Ok((selector, base, format))
}
/// Function copied with some modifications from webgpu standard <https://gpuweb.github.io/gpuweb/#copy-between-buffer-texture>
/// If successful, returns number of buffer bytes required for this copy.
/// If successful, returns (number of buffer bytes required for this copy, number of bytes between array layers).
pub(crate) fn validate_linear_texture_data(
layout: &wgt::ImageDataLayout,
format: wgt::TextureFormat,
@ -149,7 +156,7 @@ pub(crate) fn validate_linear_texture_data(
bytes_per_block: BufferAddress,
copy_size: &Extent3d,
need_copy_aligned_rows: bool,
) -> Result<BufferAddress, TransferError> {
) -> Result<(BufferAddress, BufferAddress), TransferError> {
// Convert all inputs to BufferAddress (u64) to prevent overflow issues
let copy_width = copy_size.width as BufferAddress;
let copy_height = copy_size.height as BufferAddress;
@ -202,10 +209,10 @@ pub(crate) fn validate_linear_texture_data(
}
let bytes_in_last_row = block_size * width_in_blocks;
let bytes_per_image = bytes_per_row * block_rows_per_image;
let required_bytes_in_copy = if copy_width == 0 || copy_height == 0 || copy_depth == 0 {
0
} else {
let bytes_per_image = bytes_per_row * block_rows_per_image;
let bytes_in_last_slice = bytes_per_row * (height_in_blocks - 1) + bytes_in_last_row;
bytes_per_image * (copy_depth - 1) + bytes_in_last_slice
};
@ -227,17 +234,17 @@ pub(crate) fn validate_linear_texture_data(
if copy_height > 1 && bytes_per_row < bytes_in_last_row {
return Err(TransferError::InvalidBytesPerRow);
}
Ok(required_bytes_in_copy)
Ok((required_bytes_in_copy, bytes_per_image))
}
/// Function copied with minor modifications from webgpu standard <https://gpuweb.github.io/gpuweb/#valid-texture-copy-range>
/// Returns the (virtual) mip level extent.
/// Returns the HAL copy extent and the layer count.
pub(crate) fn validate_texture_copy_range(
texture_copy_view: &ImageCopyTexture,
desc: &wgt::TextureDescriptor<()>,
texture_side: CopySide,
copy_size: &Extent3d,
) -> Result<Extent3d, TransferError> {
) -> Result<(hal::CopyExtent, u32), TransferError> {
let (block_width, block_height) = desc.format.describe().block_dimensions;
let block_width = block_width as u32;
let block_height = block_height as u32;
@ -295,7 +302,28 @@ pub(crate) fn validate_texture_copy_range(
return Err(TransferError::UnalignedCopyHeight);
}
Ok(extent_virtual)
let (depth, array_layer_count) = match desc.dimension {
wgt::TextureDimension::D1 | wgt::TextureDimension::D2 => {
(1, copy_size.depth_or_array_layers)
}
wgt::TextureDimension::D3 => (
copy_size
.depth_or_array_layers
.min(extent_virtual.depth_or_array_layers),
1,
),
};
// WebGPU uses the physical size of the texture for copies whereas vulkan uses
// the virtual size. We have passed validation, so it's safe to use the
// image extent data directly. We want the provided copy size to be no larger than
// the virtual size.
let copy_extent = hal::CopyExtent {
width: copy_size.width.min(extent_virtual.width),
height: copy_size.width.min(extent_virtual.height),
depth,
};
Ok((copy_extent, array_layer_count))
}
impl<G: GlobalIdentityHandlerFactory> Global<G> {
@ -505,13 +533,13 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let dst_barriers = dst_pending.map(|pending| pending.into_hal(dst_texture));
let format_desc = dst_texture.desc.format.describe();
let max_image_extent = validate_texture_copy_range(
let (hal_copy_size, array_layer_count) = validate_texture_copy_range(
destination,
&dst_texture.desc,
CopySide::Destination,
copy_size,
)?;
let required_buffer_bytes_in_copy = validate_linear_texture_data(
let (required_buffer_bytes_in_copy, bytes_per_array_layer) = validate_linear_texture_data(
&source.layout,
dst_texture.desc.format,
src_buffer.size,
@ -538,24 +566,22 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
);
}
// WebGPU uses the physical size of the texture for copies whereas vulkan uses
// the virtual size. We have passed validation, so it's safe to use the
// image extent data directly. We want the provided copy size to be no larger than
// the virtual size.
let region = hal::BufferTextureCopy {
buffer_layout: source.layout,
texture_base: dst_base,
size: Extent3d {
width: copy_size.width.min(max_image_extent.width),
height: copy_size.height.min(max_image_extent.height),
depth_or_array_layers: copy_size.depth_or_array_layers,
},
};
let regions = (0..array_layer_count).map(|rel_array_layer| {
let mut texture_base = dst_base.clone();
texture_base.array_layer += rel_array_layer;
let mut buffer_layout = source.layout;
buffer_layout.offset += rel_array_layer as u64 * bytes_per_array_layer;
hal::BufferTextureCopy {
buffer_layout,
texture_base,
size: hal_copy_size,
}
});
let cmd_buf_raw = cmd_buf.encoder.open();
unsafe {
cmd_buf_raw.transition_buffers(src_barriers);
cmd_buf_raw.transition_textures(dst_barriers);
cmd_buf_raw.copy_buffer_to_texture(src_raw, dst_raw, iter::once(region));
cmd_buf_raw.copy_buffer_to_texture(src_raw, dst_raw, regions);
}
Ok(())
}
@ -635,9 +661,9 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let dst_barriers = dst_pending.map(|pending| pending.into_hal(dst_buffer));
let format_desc = src_texture.desc.format.describe();
let max_image_extent =
let (hal_copy_size, array_layer_count) =
validate_texture_copy_range(source, &src_texture.desc, CopySide::Source, copy_size)?;
let required_buffer_bytes_in_copy = validate_linear_texture_data(
let (required_buffer_bytes_in_copy, bytes_per_array_layer) = validate_linear_texture_data(
&destination.layout,
src_texture.desc.format,
dst_buffer.size,
@ -667,19 +693,17 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}),
);
// WebGPU uses the physical size of the texture for copies whereas vulkan uses
// the virtual size. We have passed validation, so it's safe to use the
// image extent data directly. We want the provided copy size to be no larger than
// the virtual size.
let region = hal::BufferTextureCopy {
buffer_layout: destination.layout,
texture_base: src_base,
size: Extent3d {
width: copy_size.width.min(max_image_extent.width),
height: copy_size.height.min(max_image_extent.height),
depth_or_array_layers: copy_size.depth_or_array_layers,
},
};
let regions = (0..array_layer_count).map(|rel_array_layer| {
let mut texture_base = src_base.clone();
texture_base.array_layer += rel_array_layer;
let mut buffer_layout = destination.layout;
buffer_layout.offset += rel_array_layer as u64 * bytes_per_array_layer;
hal::BufferTextureCopy {
buffer_layout,
texture_base,
size: hal_copy_size,
}
});
let cmd_buf_raw = cmd_buf.encoder.open();
unsafe {
cmd_buf_raw.transition_buffers(dst_barriers);
@ -688,7 +712,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
src_raw,
hal::TextureUses::COPY_SRC,
dst_raw,
iter::once(region),
regions,
);
}
Ok(())
@ -725,11 +749,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
return Ok(());
}
let (src_range, src_base, _) =
let (src_range, src_tex_base, _) =
extract_texture_selector(source, copy_size, &*texture_guard)?;
let (dst_range, dst_base, _) =
let (dst_range, dst_tex_base, _) =
extract_texture_selector(destination, copy_size, &*texture_guard)?;
if src_base.aspect != dst_base.aspect {
if src_tex_base.aspect != dst_tex_base.aspect {
return Err(TransferError::MismatchedAspects.into());
}
@ -777,32 +801,31 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
barriers.extend(dst_pending.map(|pending| pending.into_hal(dst_texture)));
let max_src_image_extent =
let (src_copy_size, array_layer_count) =
validate_texture_copy_range(source, &src_texture.desc, CopySide::Source, copy_size)?;
let max_dst_image_extent = validate_texture_copy_range(
let (dst_copy_size, _) = validate_texture_copy_range(
destination,
&dst_texture.desc,
CopySide::Destination,
copy_size,
)?;
// WebGPU uses the physical size of the texture for copies whereas vulkan uses
// the virtual size. We have passed validation, so it's safe to use the
// image extent data directly. We want the provided copy size to be no larger than
// the virtual size.
let region = hal::TextureCopy {
src_base,
dst_base,
size: Extent3d {
width: copy_size
.width
.min(max_src_image_extent.width.min(max_dst_image_extent.width)),
height: copy_size
.height
.min(max_src_image_extent.height.min(max_dst_image_extent.height)),
depth_or_array_layers: copy_size.depth_or_array_layers,
},
let hal_copy_size = hal::CopyExtent {
width: src_copy_size.width.min(dst_copy_size.width),
height: src_copy_size.height.min(dst_copy_size.height),
depth: src_copy_size.depth.min(dst_copy_size.depth),
};
let regions = (0..array_layer_count).map(|rel_array_layer| {
let mut src_base = src_tex_base.clone();
let mut dst_base = dst_tex_base.clone();
src_base.array_layer += rel_array_layer;
dst_base.array_layer += rel_array_layer;
hal::TextureCopy {
src_base,
dst_base,
size: hal_copy_size,
}
});
let cmd_buf_raw = cmd_buf.encoder.open();
unsafe {
cmd_buf_raw.transition_textures(barriers.into_iter());
@ -810,7 +833,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
src_raw,
hal::TextureUses::COPY_SRC,
dst_raw,
iter::once(region),
regions,
);
}
Ok(())

View File

@ -732,10 +732,13 @@ impl<A: HalApi> Device<A> {
let usage = {
let mask_copy = !(hal::TextureUses::COPY_SRC | hal::TextureUses::COPY_DST);
let mask_dimension = match view_dim {
wgt::TextureViewDimension::Cube |
wgt::TextureViewDimension::CubeArray => hal::TextureUses::SAMPLED,
wgt::TextureViewDimension::Cube | wgt::TextureViewDimension::CubeArray => {
hal::TextureUses::SAMPLED
}
wgt::TextureViewDimension::D3 => {
hal::TextureUses::SAMPLED | hal::TextureUses::STORAGE_LOAD | hal::TextureUses::STORAGE_STORE
hal::TextureUses::SAMPLED
| hal::TextureUses::STORAGE_LOAD
| hal::TextureUses::STORAGE_STORE
}
_ => hal::TextureUses::all(),
};
@ -747,7 +750,11 @@ impl<A: HalApi> Device<A> {
texture.hal_usage & mask_copy & mask_dimension & mask_mip_level
};
log::debug!("Create view for texture {:?} filters usages to {:?}", texture_id, usage);
log::debug!(
"Create view for texture {:?} filters usages to {:?}",
texture_id,
usage
);
let hal_desc = hal::TextureViewDescriptor {
label: desc.label.borrow_option(),
format,

View File

@ -433,10 +433,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
let (texture_guard, _) = hub.textures.read(&mut token);
let (selector, texture_base, texture_format) =
let (selector, dst_base, texture_format) =
extract_texture_selector(destination, size, &*texture_guard)?;
let format_desc = texture_format.describe();
validate_linear_texture_data(
let (_, bytes_per_array_layer) = validate_linear_texture_data(
data_layout,
texture_format,
data.len() as wgt::BufferAddress,
@ -495,7 +495,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
TransferError::MissingCopyDstUsageFlag(None, Some(destination.texture)).into(),
);
}
let max_image_extent =
let (hal_copy_size, array_layer_count) =
validate_texture_copy_range(destination, &dst.desc, CopySide::Destination, size)?;
dst.life_guard.use_at(device.active_submission_index + 1);
@ -542,33 +542,29 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.map_err(DeviceError::from)?;
}
// WebGPU uses the physical size of the texture for copies whereas vulkan uses
// the virtual size. We have passed validation, so it's safe to use the
// image extent data directly. We want the provided copy size to be no larger than
// the virtual size.
let region = hal::BufferTextureCopy {
buffer_layout: wgt::ImageDataLayout {
offset: 0,
bytes_per_row: NonZeroU32::new(stage_bytes_per_row),
rows_per_image: NonZeroU32::new(block_rows_per_image),
},
texture_base,
size: wgt::Extent3d {
width: size.width.min(max_image_extent.width),
height: size.height.min(max_image_extent.height),
depth_or_array_layers: size.depth_or_array_layers,
},
};
let regions = (0..array_layer_count).map(|rel_array_layer| {
let mut texture_base = dst_base.clone();
texture_base.array_layer += rel_array_layer;
hal::BufferTextureCopy {
buffer_layout: wgt::ImageDataLayout {
offset: rel_array_layer as u64 * bytes_per_array_layer,
bytes_per_row: NonZeroU32::new(stage_bytes_per_row),
rows_per_image: NonZeroU32::new(block_rows_per_image),
},
texture_base,
size: hal_copy_size,
}
});
let barrier = hal::BufferBarrier {
buffer: &stage.buffer,
usage: hal::BufferUses::MAP_WRITE..hal::BufferUses::COPY_SRC,
};
let encoder = device.pending_writes.activate();
unsafe {
encoder.transition_buffers(iter::once(barrier));
encoder.transition_textures(transition.map(|pending| pending.into_hal(dst)));
encoder.copy_buffer_to_texture(&stage.buffer, dst_raw, iter::once(region));
encoder.copy_buffer_to_texture(&stage.buffer, dst_raw, regions);
}
device.pending_writes.consume(stage);

View File

@ -302,9 +302,14 @@ impl<A: hal::Api> Example<A> {
texture_base: hal::TextureCopyBase {
origin: wgt::Origin3d::ZERO,
mip_level: 0,
array_layer: 0,
aspect: hal::FormatAspects::COLOR,
},
size: texture_desc.size,
size: hal::CopyExtent {
width: 1,
height: 1,
depth: 1,
},
};
unsafe {
cmd_encoder.transition_buffers(iter::once(buffer_barrier));

View File

@ -210,59 +210,26 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
};
for r in regions {
let (
depth,
array_layer_count,
src_z,
src_base_array_layer,
dst_z,
dst_base_array_layer,
) = match src.dimension {
wgt::TextureDimension::D1 | wgt::TextureDimension::D2 => (
1,
r.size.depth_or_array_layers,
0,
r.src_base.origin.z,
0,
r.dst_base.origin.z,
),
wgt::TextureDimension::D3 => (
r.size.depth_or_array_layers,
1,
r.src_base.origin.z,
0,
r.dst_base.origin.z,
0,
),
};
let src_box = d3d12::D3D12_BOX {
left: r.src_base.origin.x,
top: r.src_base.origin.y,
right: r.src_base.origin.x + r.size.width,
bottom: r.src_base.origin.y + r.size.height,
front: src_z,
back: src_z + depth,
front: r.src_base.origin.z,
back: r.src_base.origin.z + r.size.depth,
};
for rel_array_layer in 0..array_layer_count {
*src_location.u.SubresourceIndex_mut() = src.calc_subresource(
r.src_base.mip_level,
src_base_array_layer + rel_array_layer,
0,
);
*dst_location.u.SubresourceIndex_mut() = dst.calc_subresource(
r.dst_base.mip_level,
dst_base_array_layer + rel_array_layer,
0,
);
list.CopyTextureRegion(
&dst_location,
r.dst_base.origin.x,
r.dst_base.origin.y,
dst_z,
&src_location,
&src_box,
);
}
*src_location.u.SubresourceIndex_mut() =
src.calc_subresource(r.src_base.mip_level, r.src_base.array_layer, 0);
*dst_location.u.SubresourceIndex_mut() =
dst.calc_subresource(r.dst_base.mip_level, r.dst_base.array_layer, 0);
list.CopyTextureRegion(
&dst_location,
r.dst_base.origin.x,
r.dst_base.origin.y,
r.dst_base.origin.z,
&src_location,
&src_box,
);
}
}
@ -285,14 +252,7 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
) where
T: Iterator<Item = crate::BufferTextureCopy>,
{
for r in regions {
let (_base_array_layer, _array_layer_count) = match src.dimension {
wgt::TextureDimension::D1 | wgt::TextureDimension::D2 => {
(r.texture_base.origin.z, r.size.depth_or_array_layers)
}
wgt::TextureDimension::D3 => (0, 1),
};
}
for _r in regions {}
}
unsafe fn begin_query(&mut self, set: &super::QuerySet, index: u32) {}

View File

@ -201,52 +201,51 @@ impl super::Queue {
ref copy,
} => {
//TODO: cubemaps
//TODO: how is depth handled?
//TODO: handle 3D copies
gl.bind_framebuffer(glow::READ_FRAMEBUFFER, Some(self.copy_fbo));
for layer in 0..copy.size.depth_or_array_layers as i32 {
if is_3d_target(src_target) {
//TODO: handle GLES without framebuffer_texture_3d
gl.framebuffer_texture_layer(
glow::READ_FRAMEBUFFER,
glow::COLOR_ATTACHMENT0,
Some(src),
copy.src_base.mip_level as i32,
copy.src_base.origin.z as i32 + layer,
);
} else {
gl.framebuffer_texture_2d(
glow::READ_FRAMEBUFFER,
glow::COLOR_ATTACHMENT0,
src_target,
Some(src),
copy.src_base.mip_level as i32,
);
}
gl.bind_texture(dst_target, Some(dst));
if is_3d_target(dst_target) {
gl.copy_tex_sub_image_3d(
dst_target,
copy.dst_base.mip_level as i32,
copy.dst_base.origin.x as i32,
copy.dst_base.origin.y as i32,
copy.dst_base.origin.z as i32 + layer,
copy.src_base.origin.x as i32,
copy.src_base.origin.y as i32,
copy.size.width as i32,
copy.size.height as i32,
);
} else {
gl.copy_tex_sub_image_2d(
dst_target,
copy.dst_base.mip_level as i32,
copy.dst_base.origin.x as i32,
copy.dst_base.origin.y as i32,
copy.src_base.origin.x as i32,
copy.src_base.origin.y as i32,
copy.size.width as i32,
copy.size.height as i32,
);
}
if is_3d_target(src_target) {
//TODO: handle GLES without framebuffer_texture_3d
gl.framebuffer_texture_layer(
glow::READ_FRAMEBUFFER,
glow::COLOR_ATTACHMENT0,
Some(src),
copy.src_base.mip_level as i32,
copy.src_base.array_layer as i32,
);
} else {
gl.framebuffer_texture_2d(
glow::READ_FRAMEBUFFER,
glow::COLOR_ATTACHMENT0,
src_target,
Some(src),
copy.src_base.mip_level as i32,
);
}
gl.bind_texture(dst_target, Some(dst));
if is_3d_target(dst_target) {
gl.copy_tex_sub_image_3d(
dst_target,
copy.dst_base.mip_level as i32,
copy.dst_base.origin.x as i32,
copy.dst_base.origin.y as i32,
copy.dst_base.origin.z as i32,
copy.src_base.origin.x as i32,
copy.src_base.origin.y as i32,
copy.size.width as i32,
copy.size.height as i32,
);
} else {
gl.copy_tex_sub_image_2d(
dst_target,
copy.dst_base.mip_level as i32,
copy.dst_base.origin.x as i32,
copy.dst_base.origin.y as i32,
copy.src_base.origin.x as i32,
copy.src_base.origin.y as i32,
copy.size.width as i32,
copy.size.height as i32,
);
}
}
C::CopyBufferToTexture {
@ -286,7 +285,7 @@ impl super::Queue {
copy.texture_base.origin.z as i32,
copy.size.width as i32,
copy.size.height as i32,
copy.size.depth_or_array_layers as i32,
copy.size.depth as i32,
format_desc.external,
format_desc.data_type,
unpack_data,
@ -306,26 +305,18 @@ impl super::Queue {
);
}
glow::TEXTURE_CUBE_MAP => {
let mut offset = copy.buffer_layout.offset as u32;
for face_index in 0..copy.size.depth_or_array_layers {
gl.tex_sub_image_2d(
CUBEMAP_FACES
[(copy.texture_base.origin.z + face_index) as usize],
copy.texture_base.mip_level as i32,
copy.texture_base.origin.x as i32,
copy.texture_base.origin.y as i32,
copy.size.width as i32,
copy.size.height as i32,
format_desc.external,
format_desc.data_type,
glow::PixelUnpackData::BufferOffset(offset),
);
offset += copy
.buffer_layout
.rows_per_image
.map_or(0, |rpi| rpi.get())
* copy.buffer_layout.bytes_per_row.map_or(0, |bpr| bpr.get());
}
let offset = copy.buffer_layout.offset as u32;
gl.tex_sub_image_2d(
CUBEMAP_FACES[copy.texture_base.array_layer as usize],
copy.texture_base.mip_level as i32,
copy.texture_base.origin.x as i32,
copy.texture_base.origin.y as i32,
copy.size.width as i32,
copy.size.height as i32,
format_desc.external,
format_desc.data_type,
glow::PixelUnpackData::BufferOffset(offset),
);
}
glow::TEXTURE_CUBE_MAP_ARRAY => {
//Note: not sure if this is correct!
@ -337,7 +328,7 @@ impl super::Queue {
copy.texture_base.origin.z as i32,
copy.size.width as i32,
copy.size.height as i32,
copy.size.depth_or_array_layers as i32,
copy.size.depth as i32,
format_desc.external,
format_desc.data_type,
unpack_data,
@ -349,10 +340,9 @@ impl super::Queue {
let bytes_per_image =
copy.buffer_layout.rows_per_image.map_or(1, |rpi| rpi.get())
* copy.buffer_layout.bytes_per_row.map_or(1, |bpr| bpr.get());
let offset_end = copy.buffer_layout.offset as u32
+ bytes_per_image * copy.size.depth_or_array_layers;
let offset = copy.buffer_layout.offset as u32;
let unpack_data = glow::CompressedPixelUnpackData::BufferRange(
copy.buffer_layout.offset as u32..offset_end,
offset..offset + bytes_per_image,
);
match dst_target {
glow::TEXTURE_3D | glow::TEXTURE_2D_ARRAY => {
@ -364,7 +354,7 @@ impl super::Queue {
copy.texture_base.origin.z as i32,
copy.size.width as i32,
copy.size.height as i32,
copy.size.depth_or_array_layers as i32,
copy.size.depth as i32,
format_desc.internal,
unpack_data,
);
@ -382,23 +372,18 @@ impl super::Queue {
);
}
glow::TEXTURE_CUBE_MAP => {
let mut offset = copy.buffer_layout.offset as u32;
for face_index in 0..copy.size.depth_or_array_layers {
gl.compressed_tex_sub_image_2d(
CUBEMAP_FACES
[(copy.texture_base.origin.z + face_index) as usize],
copy.texture_base.mip_level as i32,
copy.texture_base.origin.x as i32,
copy.texture_base.origin.y as i32,
copy.size.width as i32,
copy.size.height as i32,
format_desc.internal,
glow::CompressedPixelUnpackData::BufferRange(
offset..offset + bytes_per_image,
),
);
offset += bytes_per_image;
}
gl.compressed_tex_sub_image_2d(
CUBEMAP_FACES[copy.texture_base.array_layer as usize],
copy.texture_base.mip_level as i32,
copy.texture_base.origin.x as i32,
copy.texture_base.origin.y as i32,
copy.size.width as i32,
copy.size.height as i32,
format_desc.internal,
glow::CompressedPixelUnpackData::BufferRange(
offset..offset + bytes_per_image,
),
);
}
glow::TEXTURE_CUBE_MAP_ARRAY => {
//Note: not sure if this is correct!
@ -410,7 +395,7 @@ impl super::Queue {
copy.texture_base.origin.z as i32,
copy.size.width as i32,
copy.size.height as i32,
copy.size.depth_or_array_layers as i32,
copy.size.depth as i32,
format_desc.internal,
unpack_data,
);
@ -445,45 +430,37 @@ impl super::Queue {
.map_or(copy.size.width, |bpr| {
bpr.get() / format_info.block_size as u32
});
let column_texels = copy
.buffer_layout
.rows_per_image
.map_or(copy.size.height, |rpi| rpi.get());
gl.pixel_store_i32(glow::PACK_ROW_LENGTH, row_texels as i32);
gl.bind_buffer(glow::PIXEL_PACK_BUFFER, Some(dst));
gl.bind_framebuffer(glow::READ_FRAMEBUFFER, Some(self.copy_fbo));
for layer in 0..copy.size.depth_or_array_layers {
let offset = copy.buffer_layout.offset as u32
+ layer * column_texels * row_texels * format_info.block_size as u32;
if is_3d_target(src_target) {
//TODO: handle GLES without framebuffer_texture_3d
gl.framebuffer_texture_layer(
glow::READ_FRAMEBUFFER,
glow::COLOR_ATTACHMENT0,
Some(src),
copy.texture_base.mip_level as i32,
copy.texture_base.origin.z as i32 + layer as i32,
);
} else {
gl.framebuffer_texture_2d(
glow::READ_FRAMEBUFFER,
glow::COLOR_ATTACHMENT0,
src_target,
Some(src),
copy.texture_base.mip_level as i32,
);
}
gl.read_pixels(
copy.texture_base.origin.x as i32,
copy.texture_base.origin.y as i32,
copy.size.width as i32,
copy.size.height as i32,
format_desc.external,
format_desc.data_type,
glow::PixelPackData::BufferOffset(offset),
if is_3d_target(src_target) {
//TODO: handle GLES without framebuffer_texture_3d
gl.framebuffer_texture_layer(
glow::READ_FRAMEBUFFER,
glow::COLOR_ATTACHMENT0,
Some(src),
copy.texture_base.mip_level as i32,
copy.texture_base.array_layer as i32,
);
} else {
gl.framebuffer_texture_2d(
glow::READ_FRAMEBUFFER,
glow::COLOR_ATTACHMENT0,
src_target,
Some(src),
copy.texture_base.mip_level as i32,
);
}
gl.read_pixels(
copy.texture_base.origin.x as i32,
copy.texture_base.origin.y as i32,
copy.size.width as i32,
copy.size.height as i32,
format_desc.external,
format_desc.data_type,
glow::PixelPackData::BufferOffset(copy.buffer_layout.offset as u32),
);
}
C::SetIndexBuffer(buffer) => {
gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(buffer));

View File

@ -351,6 +351,8 @@ pub trait CommandEncoder<A: Api>: Send + Sync {
where
T: Iterator<Item = BufferCopy>;
/// Copy from one texture to another.
/// Works with a single array layer.
/// Note: `dst` current usage has to be `TextureUses::COPY_DST`.
unsafe fn copy_texture_to_texture<T>(
&mut self,
@ -361,11 +363,15 @@ pub trait CommandEncoder<A: Api>: Send + Sync {
) where
T: Iterator<Item = TextureCopy>;
/// Copy from buffer to texture.
/// Works with a single array layer.
/// Note: `dst` current usage has to be `TextureUses::COPY_DST`.
unsafe fn copy_buffer_to_texture<T>(&mut self, src: &A::Buffer, dst: &A::Texture, regions: T)
where
T: Iterator<Item = BufferTextureCopy>;
/// Copy from texture to buffer.
/// Works with a single array layer.
unsafe fn copy_texture_to_buffer<T>(
&mut self,
src: &A::Texture,
@ -1006,26 +1012,33 @@ pub struct BufferCopy {
#[derive(Clone, Debug)]
pub struct TextureCopyBase {
pub origin: wgt::Origin3d,
pub mip_level: u32,
pub array_layer: u32,
/// Origin within a texture.
/// Note: for 1D and 2D textures, Z must be 0.
pub origin: wgt::Origin3d,
pub aspect: FormatAspects,
}
//TODO: all the copy operations really want to separate
// array layers from Z, so this should not use `wgt::Extent3d`,
// and potentially work with a single layer at a time.
#[derive(Clone, Copy, Debug)]
pub struct CopyExtent {
pub width: u32,
pub height: u32,
pub depth: u32,
}
#[derive(Clone, Debug)]
pub struct TextureCopy {
pub src_base: TextureCopyBase,
pub dst_base: TextureCopyBase,
pub size: wgt::Extent3d,
pub size: CopyExtent,
}
#[derive(Clone, Debug)]
pub struct BufferTextureCopy {
pub buffer_layout: wgt::ImageDataLayout,
pub texture_base: TextureCopyBase,
pub size: wgt::Extent3d,
pub size: CopyExtent,
}
#[derive(Debug)]

View File

@ -156,22 +156,20 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
{
let encoder = self.enter_blit();
for copy in regions {
let (src_slice, src_origin) = conv::map_origin(&copy.src_base.origin, src.raw_type);
let (dst_slice, dst_origin) = conv::map_origin(&copy.dst_base.origin, dst.raw_type);
let (slice_count, extent) = conv::map_extent(&copy.size, src.raw_type);
for slice in 0..slice_count {
encoder.copy_from_texture(
&src.raw,
src_slice + slice,
copy.src_base.mip_level as u64,
src_origin,
extent,
&dst.raw,
dst_slice + slice,
copy.dst_base.mip_level as u64,
dst_origin,
);
}
let src_origin = conv::map_origin(&copy.src_base.origin);
let dst_origin = conv::map_origin(&copy.dst_base.origin);
let extent = conv::map_copy_extent(&copy.size);
encoder.copy_from_texture(
&src.raw,
copy.src_base.array_layer as u64,
copy.src_base.mip_level as u64,
src_origin,
extent,
&dst.raw,
copy.dst_base.array_layer as u64,
copy.dst_base.mip_level as u64,
dst_origin,
);
}
}
@ -185,8 +183,8 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
{
let encoder = self.enter_blit();
for copy in regions {
let (dst_slice, dst_origin) = conv::map_origin(&copy.texture_base.origin, dst.raw_type);
let (slice_count, extent) = conv::map_extent(&copy.size, dst.raw_type);
let dst_origin = conv::map_origin(&copy.texture_base.origin);
let extent = conv::map_copy_extent(&copy.size);
let bytes_per_row = copy
.buffer_layout
.bytes_per_row
@ -195,21 +193,18 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
.buffer_layout
.rows_per_image
.map_or(0, |v| v.get() as u64 * bytes_per_row);
for slice in 0..slice_count {
let offset = copy.buffer_layout.offset + bytes_per_image * slice;
encoder.copy_from_buffer_to_texture(
&src.raw,
offset,
bytes_per_row,
bytes_per_image,
extent,
&dst.raw,
dst_slice + slice,
copy.texture_base.mip_level as u64,
dst_origin,
mtl::MTLBlitOption::empty(),
);
}
encoder.copy_from_buffer_to_texture(
&src.raw,
copy.buffer_layout.offset,
bytes_per_row,
bytes_per_image,
extent,
&dst.raw,
copy.texture_base.array_layer as u64,
copy.texture_base.mip_level as u64,
dst_origin,
mtl::MTLBlitOption::empty(),
);
}
}
@ -224,8 +219,8 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
{
let encoder = self.enter_blit();
for copy in regions {
let (src_slice, src_origin) = conv::map_origin(&copy.texture_base.origin, src.raw_type);
let (slice_count, extent) = conv::map_extent(&copy.size, src.raw_type);
let src_origin = conv::map_origin(&copy.texture_base.origin);
let extent = conv::map_copy_extent(&copy.size);
let bytes_per_row = copy
.buffer_layout
.bytes_per_row
@ -234,21 +229,18 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
.buffer_layout
.rows_per_image
.map_or(0, |v| v.get() as u64 * bytes_per_row);
for slice in 0..slice_count {
let offset = copy.buffer_layout.offset + bytes_per_image * slice;
encoder.copy_from_texture_to_buffer(
&src.raw,
src_slice + slice,
copy.texture_base.mip_level as u64,
src_origin,
extent,
&dst.raw,
offset,
bytes_per_row,
bytes_per_image,
mtl::MTLBlitOption::empty(),
);
}
encoder.copy_from_texture_to_buffer(
&src.raw,
copy.texture_base.array_layer as u64,
copy.texture_base.mip_level as u64,
src_origin,
extent,
&dst.raw,
copy.buffer_layout.offset,
bytes_per_row,
bytes_per_image,
mtl::MTLBlitOption::empty(),
);
}
}

View File

@ -261,34 +261,20 @@ pub fn map_range(range: &crate::MemoryRange) -> mtl::NSRange {
}
}
pub fn map_extent(extent: &wgt::Extent3d, raw_type: mtl::MTLTextureType) -> (u64, mtl::MTLSize) {
let (depth, array_layers) = match raw_type {
mtl::MTLTextureType::D3 => (extent.depth_or_array_layers as u64, 1),
_ => (1, extent.depth_or_array_layers as u64),
};
(
array_layers,
mtl::MTLSize {
width: extent.width as u64,
height: extent.height as u64,
depth,
},
)
pub fn map_copy_extent(extent: &crate::CopyExtent) -> mtl::MTLSize {
mtl::MTLSize {
width: extent.width as u64,
height: extent.height as u64,
depth: extent.depth as u64,
}
}
pub fn map_origin(origin: &wgt::Origin3d, raw_type: mtl::MTLTextureType) -> (u64, mtl::MTLOrigin) {
let (z, slice) = match raw_type {
mtl::MTLTextureType::D3 => (origin.z as u64, 0),
_ => (0, origin.z as u64),
};
(
slice,
mtl::MTLOrigin {
x: origin.x as u64,
y: origin.y as u64,
z,
},
)
pub fn map_origin(origin: &wgt::Origin3d) -> mtl::MTLOrigin {
mtl::MTLOrigin {
x: origin.x as u64,
y: origin.y as u64,
z: origin.z as u64,
}
}
pub fn map_store_action(store: bool, resolve: bool) -> mtl::MTLStoreAction {

View File

@ -18,13 +18,11 @@ impl super::Texture {
where
T: Iterator<Item = crate::BufferTextureCopy>,
{
let dim = self.dim;
let aspects = self.aspects;
let fi = self.format_info;
regions.map(move |r| {
let (layer_count, image_extent) = conv::map_extent(r.size, dim);
let (image_subresource, image_offset) =
conv::map_subresource_layers(&r.texture_base, dim, aspects, layer_count);
conv::map_subresource_layers(&r.texture_base, aspects);
vk::BufferImageCopy {
buffer_offset: r.buffer_layout.offset,
buffer_row_length: r.buffer_layout.bytes_per_row.map_or(0, |bpr| {
@ -36,7 +34,7 @@ impl super::Texture {
.map_or(0, |rpi| rpi.get() * fi.block_dimensions.1 as u32),
image_subresource,
image_offset,
image_extent,
image_extent: conv::map_copy_extent(&r.size),
}
})
}
@ -228,17 +226,16 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
let src_layout = conv::derive_image_layout(src_usage, src.aspects);
let vk_regions_iter = regions.map(|r| {
let (layer_count, extent) = conv::map_extent(r.size, src.dim);
let (src_subresource, src_offset) =
conv::map_subresource_layers(&r.src_base, src.dim, src.aspects, layer_count);
conv::map_subresource_layers(&r.src_base, src.aspects);
let (dst_subresource, dst_offset) =
conv::map_subresource_layers(&r.dst_base, dst.dim, dst.aspects, layer_count);
conv::map_subresource_layers(&r.dst_base, dst.aspects);
vk::ImageCopy {
src_subresource,
src_offset,
dst_subresource,
dst_offset,
extent,
extent: conv::map_copy_extent(&r.size),
}
});

View File

@ -352,42 +352,6 @@ pub fn map_aspects(aspects: crate::FormatAspects) -> vk::ImageAspectFlags {
flags
}
pub fn map_origin(
origin: wgt::Origin3d,
texture_dim: wgt::TextureDimension,
) -> (u32, vk::Offset3D) {
let (z, array_layer) = match texture_dim {
wgt::TextureDimension::D3 => (origin.z as i32, 0),
_ => (0, origin.z),
};
(
array_layer,
vk::Offset3D {
x: origin.x as i32,
y: origin.y as i32,
z,
},
)
}
pub fn map_extent(
extent: wgt::Extent3d,
texture_dim: wgt::TextureDimension,
) -> (u32, vk::Extent3D) {
let (depth, array_layers) = match texture_dim {
wgt::TextureDimension::D3 => (extent.depth_or_array_layers, 1),
_ => (1, extent.depth_or_array_layers),
};
(
array_layers,
vk::Extent3D {
width: extent.width,
height: extent.height,
depth,
},
)
}
pub fn map_attachment_ops(
op: crate::AttachmentOps,
) -> (vk::AttachmentLoadOp, vk::AttachmentStoreOp) {
@ -541,6 +505,14 @@ pub fn map_view_dimension(dim: wgt::TextureViewDimension) -> vk::ImageViewType {
}
}
pub fn map_copy_extent(extent: &crate::CopyExtent) -> vk::Extent3D {
vk::Extent3D {
width: extent.width,
height: extent.height,
depth: extent.depth,
}
}
pub fn map_subresource_range(
range: &wgt::ImageSubresourceRange,
texture_aspect: crate::FormatAspects,
@ -560,16 +532,18 @@ pub fn map_subresource_range(
pub fn map_subresource_layers(
base: &crate::TextureCopyBase,
texture_dim: wgt::TextureDimension,
texture_aspect: crate::FormatAspects,
layer_count: u32,
) -> (vk::ImageSubresourceLayers, vk::Offset3D) {
let (base_array_layer, offset) = map_origin(base.origin, texture_dim);
let offset = vk::Offset3D {
x: base.origin.x as i32,
y: base.origin.y as i32,
z: base.origin.z as i32,
};
let subresource = vk::ImageSubresourceLayers {
aspect_mask: map_aspects(base.aspect & texture_aspect),
mip_level: base.mip_level,
base_array_layer,
layer_count,
base_array_layer: base.array_layer,
layer_count: 1,
};
(subresource, offset)
}

View File

@ -654,7 +654,11 @@ impl crate::Device<super::Api> for super::Device {
&self,
desc: &crate::TextureDescriptor,
) -> Result<super::Texture, crate::DeviceError> {
let (array_layer_count, vk_extent) = conv::map_extent(desc.size, desc.dimension);
let (depth, array_layer_count) = match desc.dimension {
wgt::TextureDimension::D3 => (desc.size.depth_or_array_layers, 1),
_ => (1, desc.size.depth_or_array_layers),
};
let mut raw_flags = vk::ImageCreateFlags::empty();
if desc.dimension == wgt::TextureDimension::D2 && desc.size.depth_or_array_layers % 6 == 0 {
raw_flags |= vk::ImageCreateFlags::CUBE_COMPATIBLE;
@ -664,7 +668,11 @@ impl crate::Device<super::Api> for super::Device {
.flags(raw_flags)
.image_type(conv::map_texture_dimension(desc.dimension))
.format(self.shared.private_caps.map_texture_format(desc.format))
.extent(vk_extent)
.extent(vk::Extent3D {
width: desc.size.width,
height: desc.size.height,
depth,
})
.mip_levels(desc.mip_level_count)
.array_layers(array_layer_count)
.samples(vk::SampleCountFlags::from_raw(desc.sample_count))
@ -699,7 +707,6 @@ impl crate::Device<super::Api> for super::Device {
raw,
block: Some(block),
usage: desc.usage,
dim: desc.dimension,
aspects: crate::FormatAspects::from(desc.format),
format_info: desc.format.describe(),
raw_flags,

View File

@ -647,7 +647,6 @@ impl crate::Surface<super::Api> for super::Surface {
raw: sc.images[index as usize],
block: None,
usage: sc.config.usage,
dim: wgt::TextureDimension::D2,
aspects: crate::FormatAspects::COLOR,
format_info: sc.config.format.describe(),
raw_flags: vk::ImageCreateFlags::empty(),

View File

@ -255,7 +255,6 @@ pub struct Texture {
raw: vk::Image,
block: Option<gpu_alloc::MemoryBlock<vk::DeviceMemory>>,
usage: crate::TextureUses,
dim: wgt::TextureDimension,
aspects: crate::FormatAspects,
format_info: wgt::TextureFormatInfo,
raw_flags: vk::ImageCreateFlags,