Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

hal: switch texture copies to expect physical sizes #1908

Merged
merged 1 commit into from
Sep 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
51 changes: 51 additions & 0 deletions wgpu-hal/src/auxil/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,54 @@ pub fn map_naga_stage(stage: naga::ShaderStage) -> wgt::ShaderStages {
naga::ShaderStage::Compute => wgt::ShaderStages::COMPUTE,
}
}

impl crate::CopyExtent {
pub 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),
}
}

// 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.
pub 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),
}
}
}

impl crate::TextureCopyBase {
pub 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 {
pub 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 {
pub 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);
}
}
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
9 changes: 6 additions & 3 deletions wgpu-hal/src/gles/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,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 +311,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 +334,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
3 changes: 3 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
30 changes: 16 additions & 14 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,6 +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);
// no clamping is done: Metal expects physical sizes here
let extent = conv::map_copy_extent(&copy.size);
encoder.copy_from_texture(
&src.raw,
Expand All @@ -265,7 +259,11 @@ 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);
// Metal expects buffer-texture copies in virtual sizes
let extent = copy
.texture_base
.max_copy_size(&dst.copy_size)
.min(&copy.size);
let bytes_per_row = copy
.buffer_layout
.bytes_per_row
Expand All @@ -279,7 +277,7 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
copy.buffer_layout.offset,
bytes_per_row,
bytes_per_image,
extent,
conv::map_copy_extent(&extent),
&dst.raw,
copy.texture_base.array_layer as u64,
copy.texture_base.mip_level as u64,
Expand All @@ -301,7 +299,11 @@ 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);
// Metal expects texture-buffer copies in virtual sizes
let extent = copy
.texture_base
.max_copy_size(&src.copy_size)
.min(&copy.size);
let bytes_per_row = copy
.buffer_layout
.bytes_per_row
Expand All @@ -315,7 +317,7 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
copy.texture_base.array_layer as u64,
copy.texture_base.mip_level as u64,
src_origin,
extent,
conv::map_copy_extent(&extent),
&dst.raw,
copy.buffer_layout.offset,
bytes_per_row,
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
3 changes: 2 additions & 1 deletion wgpu-hal/src/metal/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ pub struct Surface {
render_layer: Mutex<mtl::MetalLayer>,
swapchain_format: wgt::TextureFormat,
raw_swapchain_format: mtl::MTLPixelFormat,
extent: wgt::Extent3d,
main_thread_id: thread::ThreadId,
// Useful for UI-intensive applications that are sensitive to
// window resizing.
Expand Down Expand Up @@ -425,7 +426,7 @@ pub struct Texture {
raw_type: mtl::MTLTextureType,
array_layers: u32,
mip_levels: u32,
size: wgt::Extent3d,
copy_size: crate::CopyExtent,
}

unsafe impl Send for Texture {}
Expand Down
Loading