Skip to content

Commit

Permalink
hal: switch texture copies to expect physical sizes
Browse files Browse the repository at this point in the history
  • Loading branch information
kvark committed Sep 7, 2021
1 parent df2a686 commit 62a17a3
Show file tree
Hide file tree
Showing 17 changed files with 178 additions and 70 deletions.
15 changes: 3 additions & 12 deletions wgpu-core/src/command/transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,21 +339,12 @@ pub(crate) fn validate_texture_copy_range(
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,
),
wgt::TextureDimension::D3 => (copy_size.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.height.min(extent_virtual.height),
width: copy_size.width,
height: copy_size.height,
depth,
};
Ok((copy_extent, array_layer_count))
Expand Down
37 changes: 9 additions & 28 deletions wgpu-hal/src/dx12/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,6 @@ fn make_box(origin: &wgt::Origin3d, size: &crate::CopyExtent) -> d3d12::D3D12_BO
}
}

fn upround_extent(size: crate::CopyExtent, block_size: u32) -> crate::CopyExtent {
debug_assert!(block_size.is_power_of_two());

let block_mask = block_size - 1;

crate::CopyExtent {
width: (size.width + block_mask) & !(block_mask),
height: (size.height + block_mask) & !(block_mask),
depth: size.depth,
}
}

impl super::Temp {
fn prepare_marker(&mut self, marker: &str) -> (&[u16], u32) {
self.marker.clear();
Expand Down Expand Up @@ -514,11 +502,8 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
u: mem::zeroed(),
};

let block_size = src.format.describe().block_dimensions.0 as u32;
for r in regions {
let uprounded_size = upround_extent(r.size, block_size);

let src_box = make_box(&r.src_base.origin, &uprounded_size);
let src_box = make_box(&r.src_base.origin, &r.size);
*src_location.u.SubresourceIndex_mut() = src.calc_subresource_for_copy(&r.src_base);
*dst_location.u.SubresourceIndex_mut() = dst.calc_subresource_for_copy(&r.dst_base);

Expand Down Expand Up @@ -556,19 +541,17 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {

let block_size = dst.format.describe().block_dimensions.0 as u32;
for r in regions {
let uprounded_size = upround_extent(r.size, block_size);

let src_box = make_box(&wgt::Origin3d::ZERO, &uprounded_size);
let src_box = make_box(&wgt::Origin3d::ZERO, &r.size);
*src_location.u.PlacedFootprint_mut() = d3d12::D3D12_PLACED_SUBRESOURCE_FOOTPRINT {
Offset: r.buffer_layout.offset,
Footprint: d3d12::D3D12_SUBRESOURCE_FOOTPRINT {
Format: raw_format,
Width: uprounded_size.width,
Width: r.size.width,
Height: r
.buffer_layout
.rows_per_image
.map_or(uprounded_size.height, |count| count.get() * block_size),
Depth: uprounded_size.depth,
.map_or(r.size.height, |count| count.get() * block_size),
Depth: r.size.depth,
RowPitch: r.buffer_layout.bytes_per_row.map_or(0, |count| {
count.get().max(d3d12::D3D12_TEXTURE_DATA_PITCH_ALIGNMENT)
}),
Expand Down Expand Up @@ -611,20 +594,18 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {

let block_size = src.format.describe().block_dimensions.0 as u32;
for r in regions {
let uprounded_size = upround_extent(r.size, block_size);

let src_box = make_box(&r.texture_base.origin, &uprounded_size);
let src_box = make_box(&r.texture_base.origin, &r.size);
*src_location.u.SubresourceIndex_mut() = src.calc_subresource_for_copy(&r.texture_base);
*dst_location.u.PlacedFootprint_mut() = d3d12::D3D12_PLACED_SUBRESOURCE_FOOTPRINT {
Offset: r.buffer_layout.offset,
Footprint: d3d12::D3D12_SUBRESOURCE_FOOTPRINT {
Format: raw_format,
Width: uprounded_size.width,
Width: r.size.width,
Height: r
.buffer_layout
.rows_per_image
.map_or(uprounded_size.height, |count| count.get() * block_size),
Depth: uprounded_size.depth,
.map_or(r.size.height, |count| count.get() * block_size),
Depth: r.size.depth,
RowPitch: r.buffer_layout.bytes_per_row.map_or(0, |count| count.get()),
},
};
Expand Down
49 changes: 46 additions & 3 deletions wgpu-hal/src/gles/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,46 @@ pub(super) struct State {
dirty_vbuf_mask: usize,
}

impl crate::CopyExtent {
fn min(&self, other: &Self) -> Self {
Self {
width: self.width.min(other.width),
height: self.height.min(other.height),
depth: self.depth.min(other.depth),
}
}
}

impl crate::TextureCopyBase {
fn max_copy_size(&self, full_size: &crate::CopyExtent) -> crate::CopyExtent {
let mip = full_size.at_mip_level(self.mip_level);
crate::CopyExtent {
width: mip.width - self.origin.x,
height: mip.height - self.origin.y,
depth: mip.depth - self.origin.z,
}
}
}

impl crate::BufferTextureCopy {
fn clamp_size_to_virtual(&mut self, full_size: &crate::CopyExtent) {
let max_size = self.texture_base.max_copy_size(full_size);
self.size = self.size.min(max_size);
}
}

impl crate::TextureCopy {
fn clamp_size_to_virtual(
&mut self,
full_src_size: &crate::CopyExtent,
full_dst_size: &crate::CopyExtent,
) {
let max_src_size = self.src_base.max_copy_size(full_src_size);
let max_dst_size = self.dst_base.max_copy_size(full_dst_size);
self.size = self.size.min(&max_src_size).min(&max_dst_size);
}
}

impl super::CommandBuffer {
fn clear(&mut self) {
self.label = None;
Expand Down Expand Up @@ -290,7 +330,8 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
{
let (src_raw, src_target) = src.inner.as_native();
let (dst_raw, dst_target) = dst.inner.as_native();
for copy in regions {
for mut copy in regions {
copy.clamp_size_to_virtual(&src.copy_size, &dst.copy_size);
self.cmd_buffer.commands.push(C::CopyTextureToTexture {
src: src_raw,
src_target,
Expand All @@ -310,7 +351,8 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
T: Iterator<Item = crate::BufferTextureCopy>,
{
let (dst_raw, dst_target) = dst.inner.as_native();
for copy in regions {
for mut copy in regions {
copy.clamp_size_to_virtual(&dst.copy_size);
self.cmd_buffer.commands.push(C::CopyBufferToTexture {
src: src.raw,
src_target: src.target,
Expand All @@ -332,7 +374,8 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
T: Iterator<Item = crate::BufferTextureCopy>,
{
let (src_raw, src_target) = src.inner.as_native();
for copy in regions {
for mut copy in regions {
copy.clamp_size_to_virtual(&src.copy_size);
self.cmd_buffer.commands.push(C::CopyTextureToBuffer {
src: src_raw,
src_target,
Expand Down
8 changes: 8 additions & 0 deletions wgpu-hal/src/gles/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,12 @@ impl crate::Device<super::Api> for super::Device {
| crate::TextureUses::DEPTH_STENCIL_READ;
let format_desc = self.shared.describe_texture_format(desc.format);

let mut copy_size = crate::CopyExtent {
width: desc.size.width,
height: desc.size.height,
depth: 1,
};

let inner = if render_usage.contains(desc.usage)
&& desc.dimension == wgt::TextureDimension::D2
&& desc.size.depth_or_array_layers == 1
Expand Down Expand Up @@ -516,6 +522,7 @@ impl crate::Device<super::Api> for super::Device {
}
}
wgt::TextureDimension::D3 => {
copy_size.depth = desc.size.depth_or_array_layers;
let target = glow::TEXTURE_3D;
gl.bind_texture(target, Some(raw));
gl.tex_storage_3d(
Expand Down Expand Up @@ -562,6 +569,7 @@ impl crate::Device<super::Api> for super::Device {
},
format: desc.format,
format_desc,
copy_size,
})
}
unsafe fn destroy_texture(&self, texture: super::Texture) {
Expand Down
5 changes: 5 additions & 0 deletions wgpu-hal/src/gles/egl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -984,6 +984,11 @@ impl crate::Surface<super::Api> for Surface {
mip_level_count: 1,
format: sc.format,
format_desc: sc.format_desc.clone(),
copy_size: crate::CopyExtent {
width: sc.extent.width,
height: sc.extent.height,
depth: 1,
},
};
Ok(Some(crate::AcquiredSurfaceTexture {
texture,
Expand Down
1 change: 1 addition & 0 deletions wgpu-hal/src/gles/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ pub struct Texture {
array_layer_count: u32,
format: wgt::TextureFormat,
format_desc: TextureFormatDesc,
copy_size: crate::CopyExtent,
}

#[derive(Clone, Debug)]
Expand Down
16 changes: 16 additions & 0 deletions wgpu-hal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,7 @@ pub trait CommandEncoder<A: Api>: Send + Sync {
/// Copy from one texture to another.
/// Works with a single array layer.
/// Note: `dst` current usage has to be `TextureUses::COPY_DST`.
/// Note: the copy extent is in physical size (rounded to the block size)
unsafe fn copy_texture_to_texture<T>(
&mut self,
src: &A::Texture,
Expand All @@ -372,12 +373,14 @@ pub trait CommandEncoder<A: Api>: Send + Sync {
/// Copy from buffer to texture.
/// Works with a single array layer.
/// Note: `dst` current usage has to be `TextureUses::COPY_DST`.
/// Note: the copy extent is in physical size (rounded to the block size)
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.
/// Note: the copy extent is in physical size (rounded to the block size)
unsafe fn copy_texture_to_buffer<T>(
&mut self,
src: &A::Texture,
Expand Down Expand Up @@ -1046,6 +1049,19 @@ pub struct CopyExtent {
pub depth: u32,
}

impl CopyExtent {
// Get the copy size at a specific mipmap level. This doesn't make most sense,
// since the copy extents are provided *for* a mipmap level to start with.
// But backends use `CopyExtent` more sparingly, and this piece is shared.
fn at_mip_level(&self, level: u32) -> Self {
Self {
width: (self.width >> level).max(1),
height: (self.height >> level).max(1),
depth: (self.depth >> level).max(1),
}
}
}

#[derive(Clone, Debug)]
pub struct TextureCopy {
pub src_base: TextureCopyBase,
Expand Down
21 changes: 8 additions & 13 deletions wgpu-hal/src/metal/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,26 +153,19 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {

for mip_level in mip_range {
// Note that Metal requires this only to be a multiple of the pixel size, not some other constant like in other APIs.
let mip_size = texture
.size
.mip_level_size(mip_level, texture.raw_type == mtl::MTLTextureType::D3);
let depth = if texture.raw_type == mtl::MTLTextureType::D3 {
mip_size.depth_or_array_layers as u64
} else {
1
};
let mip_size = texture.copy_size.at_mip_level(mip_level);
let bytes_per_row = mip_size.width as u64 / format_desc.block_dimensions.0 as u64
* format_desc.block_size as u64;
let max_rows_per_copy = super::ZERO_BUFFER_SIZE / bytes_per_row;
// round down to a multiple of rows needed by the texture format
let max_rows_per_copy = max_rows_per_copy / format_desc.block_dimensions.1 as u64
* format_desc.block_dimensions.1 as u64;
assert!(max_rows_per_copy > 0, "Zero buffer size is too small to fill a single row of a texture of type {:?}, size {:?} and format {:?}",
texture.raw_type, texture.size, texture.format);
texture.raw_type, texture.copy_size, texture.format);

for array_layer in array_range.clone() {
// 3D textures are quickly massive in memory size, so we don't bother trying to do more than one layer at once.
for z in 0..depth {
for z in 0..mip_size.depth as u64 {
// May need multiple copies for each subresource! We assume that we never need to split a row.
let mut num_rows_left = mip_size.height as u64;
while num_rows_left > 0 {
Expand Down Expand Up @@ -239,7 +232,7 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
for copy in regions {
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);
let extent = conv::map_texture_copy_extent(&copy.size);
encoder.copy_from_texture(
&src.raw,
copy.src_base.array_layer as u64,
Expand All @@ -265,7 +258,8 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
let encoder = self.enter_blit();
for copy in regions {
let dst_origin = conv::map_origin(&copy.texture_base.origin);
let extent = conv::map_copy_extent(&copy.size);
let extent =
conv::map_buffer_copy_extent(&copy.size, &copy.texture_base, &dst.copy_size);
let bytes_per_row = copy
.buffer_layout
.bytes_per_row
Expand Down Expand Up @@ -301,7 +295,8 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
let encoder = self.enter_blit();
for copy in regions {
let src_origin = conv::map_origin(&copy.texture_base.origin);
let extent = conv::map_copy_extent(&copy.size);
let extent =
conv::map_buffer_copy_extent(&copy.size, &copy.texture_base, &src.copy_size);
let bytes_per_row = copy
.buffer_layout
.bytes_per_row
Expand Down
18 changes: 17 additions & 1 deletion wgpu-hal/src/metal/conv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,23 @@ pub fn map_range(range: &crate::MemoryRange) -> mtl::NSRange {
}
}

pub fn map_copy_extent(extent: &crate::CopyExtent) -> mtl::MTLSize {
pub fn map_buffer_copy_extent(
extent: &crate::CopyExtent,
base: &crate::TextureCopyBase,
full: &crate::CopyExtent,
) -> mtl::MTLSize {
let mip = full.at_mip_level(base.mip_level);
// wgpu-hal API works with physical sizes of textures (aligned to the block size),
// but Metal expects virtual size (unaligned) specifically for copies involving buffers.
// Therefore, we cut the sizes to the virtual size here.
mtl::MTLSize {
width: extent.width.min(mip.width - base.origin.x) as u64,
height: extent.height.min(mip.height - base.origin.y) as u64,
depth: extent.depth.min(mip.depth - base.origin.z) as u64,
}
}

pub fn map_texture_copy_extent(extent: &crate::CopyExtent) -> mtl::MTLSize {
mtl::MTLSize {
width: extent.width as u64,
height: extent.height as u64,
Expand Down
8 changes: 7 additions & 1 deletion wgpu-hal/src/metal/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,11 @@ impl crate::Device<super::Api> for super::Device {

let descriptor = mtl::TextureDescriptor::new();
let mut array_layers = desc.size.depth_or_array_layers;
let mut copy_size = crate::CopyExtent {
width: desc.size.width,
height: desc.size.height,
depth: 1,
};
let mtl_type = match desc.dimension {
wgt::TextureDimension::D1 => {
if desc.size.depth_or_array_layers > 1 {
Expand All @@ -259,6 +264,7 @@ impl crate::Device<super::Api> for super::Device {
wgt::TextureDimension::D3 => {
descriptor.set_depth(desc.size.depth_or_array_layers as u64);
array_layers = 1;
copy_size.depth = desc.size.depth_or_array_layers;
mtl::MTLTextureType::D3
}
};
Expand All @@ -283,7 +289,7 @@ impl crate::Device<super::Api> for super::Device {
raw_type: mtl_type,
mip_levels: desc.mip_level_count,
array_layers,
size: desc.size,
copy_size,
})
}

Expand Down
Loading

0 comments on commit 62a17a3

Please sign in to comment.