832: Return errors from device functions r=kvark a=GabrielMajeri

**Connections**
Part of #638 

**Description**
Lots of changes, but they should be easily reviewable commit-by-commit.

- Error types for most of the fallible resource creation errors in `device/mod.rs`

- Removed assertions and replaced them with error types

- All of the `BufferMap`, `BufferNotMapped` etc. errors were united in a single `BufferAccessError`, since it was pretty weird to have so many overlapping error types.

- Removed all `unwrap`s of `gfx-hal` errors - they are now returned to the caller.

**Testing**
Checked with core and tested with `wgpu-rs` (see https://github.com/gfx-rs/wgpu-rs/pull/469)

Co-authored-by: Gabriel Majeri <gabriel.majeri6@gmail.com>
This commit is contained in:
bors[bot] 2020-07-24 20:43:56 +00:00 committed by GitHub
commit 102d69eb58
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 348 additions and 229 deletions

View File

@ -181,15 +181,17 @@ impl GlobalPlay for wgc::hub::Global<IdentityPassThroughFactory> {
parent_id,
desc.map(|d| d.map_label(|_| label.as_ptr())).as_ref(),
id,
);
)
.unwrap();
}
A::DestroyTextureView(id) => {
self.texture_view_destroy::<B>(id);
self.texture_view_destroy::<B>(id).unwrap();
}
A::CreateSampler(id, desc) => {
let label = Label::new(&desc.label);
self.device_maintain_ids::<B>(device);
self.device_create_sampler::<B>(device, &desc.map_label(|_| label.as_ptr()), id);
self.device_create_sampler::<B>(device, &desc.map_label(|_| label.as_ptr()), id)
.unwrap();
}
A::DestroySampler(id) => {
self.sampler_destroy::<B>(id);

View File

@ -6,6 +6,7 @@ use crate::{
device::SHADER_STAGE_COUNT,
id::{BindGroupLayoutId, BufferId, DeviceId, SamplerId, TextureViewId},
track::{TrackerSet, DUMMY_SELECTOR},
validation::{MissingBufferUsageError, MissingTextureUsageError},
FastHashMap, LifeGuard, MultiRefCount, RefCount, Stored, MAX_BIND_GROUPS,
};
@ -25,25 +26,53 @@ use std::{
use thiserror::Error;
#[derive(Clone, Debug, Error)]
pub enum BindGroupLayoutError {
pub enum CreateBindGroupLayoutError {
#[error("arrays of bindings unsupported for this type of binding")]
ArrayUnsupported,
#[error("conflicting binding at index {0}")]
ConflictBinding(u32),
#[error("required device feature is missing: {0:?}")]
MissingFeature(wgt::Features),
#[error("arrays of bindings can't be 0 elements long")]
ZeroCount,
#[error("arrays of bindings unsupported for this type of binding")]
ArrayUnsupported,
#[error("not enough memory left")]
OutOfMemory,
#[error(transparent)]
TooManyBindings(BindingTypeMaxCountError),
#[error("arrays of bindings can't be 0 elements long")]
ZeroCount,
}
#[derive(Clone, Debug, Error)]
pub enum CreateBindGroupError {
#[error("binding count declared with {expected} items, but {actual} items were provided")]
BindingArrayLengthMismatch { actual: usize, expected: usize },
#[error("bound buffer range {range:?} does not fit in buffer of size {size}")]
BindingRangeTooLarge {
range: Range<wgt::BufferAddress>,
size: u64,
},
#[error("buffer binding size {actual} is less than minimum {min}")]
BindingSizeTooSmall { actual: u64, min: u64 },
#[error("number of bindings in bind group descriptor ({actual}) does not match the number of bindings defined in the bind group layout ({expected})")]
BindingsNumMismatch { actual: usize, expected: usize },
#[error("unable to find a corresponding declaration for the given binding {0}")]
MissingBindingDeclaration(u32),
#[error(transparent)]
MissingBufferUsage(#[from] MissingBufferUsageError),
#[error(transparent)]
MissingTextureUsage(#[from] MissingTextureUsageError),
#[error("required device features not enabled: {0:?}")]
MissingFeatures(wgt::Features),
#[error("binding declared as a single item, but bind group is using it as an array")]
SingleBindingExpected,
#[error("unable to create a bind group with a swap chain image")]
SwapChainImage,
#[error(
"buffer offset {0} must be a multiple of {}",
wgt::BIND_BUFFER_ALIGNMENT
)]
UnalignedBufferOffset(wgt::BufferAddress),
#[error("uniform buffer binding range exceeds `max_uniform_buffer_binding_size` limit")]
UniformBufferRangeTooLarge,
#[error("binding {binding} has a different type ({actual:?}) than the one in the layout ({expected:?})")]
WrongBindingType {
// Index of the binding
@ -55,8 +84,6 @@ pub enum CreateBindGroupError {
},
#[error("the given sampler is/is not a comparison sampler, while the layout type indicates otherwise")]
WrongSamplerComparison,
#[error("uniform buffer binding range exceeds `max_uniform_buffer_binding_size` limit")]
UniformBufferRangeTooLarge,
}
#[derive(Clone, Debug, Error)]
@ -239,23 +266,7 @@ pub struct BindGroupLayout<B: hal::Backend> {
}
#[derive(Clone, Debug, Error)]
pub enum PipelineLayoutError {
#[error("bind group layout count {actual} exceeds device bind group limit {max}")]
TooManyGroups { actual: usize, max: usize },
#[error(transparent)]
TooManyBindings(BindingTypeMaxCountError),
#[error("push constant at index {index} has range {}..{} which exceeds device push constant size limit 0..{max}", range.start, range.end)]
PushConstantRangeTooLarge {
index: usize,
range: Range<u32>,
max: u32,
},
#[error("push constant range (index {index}) provides for stage(s) {provided:?} but there exists another range that provides stage(s) {intersected:?}. Each stage may only be provided by one range")]
MoreThanOnePushConstantRangePerStage {
index: usize,
provided: wgt::ShaderStage,
intersected: wgt::ShaderStage,
},
pub enum CreatePipelineLayoutError {
#[error(
"push constant at index {index} has range bound {bound} not aligned to {}",
wgt::PUSH_CONSTANT_ALIGNMENT
@ -263,6 +274,24 @@ pub enum PipelineLayoutError {
MisalignedPushConstantRange { index: usize, bound: u32 },
#[error("device does not have required feature: {0:?}")]
MissingFeature(wgt::Features),
#[error("push constant range (index {index}) provides for stage(s) {provided:?} but there exists another range that provides stage(s) {intersected:?}. Each stage may only be provided by one range")]
MoreThanOnePushConstantRangePerStage {
index: usize,
provided: wgt::ShaderStage,
intersected: wgt::ShaderStage,
},
#[error("not enough memory left")]
OutOfMemory,
#[error("push constant at index {index} has range {}..{} which exceeds device push constant size limit 0..{max}", range.start, range.end)]
PushConstantRangeTooLarge {
index: usize,
range: Range<u32>,
max: u32,
},
#[error(transparent)]
TooManyBindings(BindingTypeMaxCountError),
#[error("bind group layout count {actual} exceeds device bind group limit {max}")]
TooManyGroups { actual: usize, max: usize },
}
#[derive(Clone, Debug, Error)]

View File

@ -3,13 +3,13 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use crate::{
binding_model::{self, CreateBindGroupError, PipelineLayoutError},
binding_model::{self, CreateBindGroupError, CreatePipelineLayoutError},
command, conv,
device::life::WaitIdleError,
hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Hub, Input, Token},
id, pipeline, resource, span, swap_chain,
track::{BufferState, TextureState, TrackerSet},
validation::{self, check_buffer_usage, MissingBufferUsageError},
validation::{self, check_buffer_usage, check_texture_usage, MissingBufferUsageError},
FastHashMap, LifeGuard, MultiRefCount, PrivateFeatures, Stored, SubmissionIndex,
MAX_BIND_GROUPS,
};
@ -131,7 +131,7 @@ fn map_buffer<B: hal::Backend>(
buffer: &mut resource::Buffer<B>,
sub_range: hal::buffer::SubRange,
kind: HostMap,
) -> Result<ptr::NonNull<u8>, BufferMapError> {
) -> Result<ptr::NonNull<u8>, BufferAccessError> {
let (ptr, segment, needs_sync) = {
let segment = hal::memory::Segment {
offset: sub_range.offset,
@ -149,7 +149,7 @@ fn map_buffer<B: hal::Backend>(
buffer.sync_mapped_writes = match kind {
HostMap::Read if needs_sync => unsafe {
raw.invalidate_mapped_memory_ranges(iter::once((buffer.memory.memory(), segment)))
.unwrap();
.or(Err(BufferAccessError::OutOfMemory))?;
None
},
HostMap::Write if needs_sync => Some(segment),
@ -158,13 +158,17 @@ fn map_buffer<B: hal::Backend>(
Ok(ptr)
}
fn unmap_buffer<B: hal::Backend>(raw: &B::Device, buffer: &mut resource::Buffer<B>) {
fn unmap_buffer<B: hal::Backend>(
raw: &B::Device,
buffer: &mut resource::Buffer<B>,
) -> Result<(), BufferAccessError> {
if let Some(segment) = buffer.sync_mapped_writes.take() {
unsafe {
raw.flush_mapped_memory_ranges(iter::once((buffer.memory.memory(), segment)))
.unwrap()
};
.or(Err(BufferAccessError::OutOfMemory))?;
}
}
Ok(())
}
//Note: this logic is specifically moved out of `handle_mapping()` in order to
@ -415,7 +419,14 @@ impl<B: GfxBackend> Device<B> {
}
};
let mut buffer = unsafe { self.raw.create_buffer(desc.size.max(1), usage).unwrap() };
let mut buffer = unsafe {
self.raw
.create_buffer(desc.size.max(1), usage)
.map_err(|err| match err {
hal::buffer::CreationError::OutOfMemory(_) => CreateBufferError::OutOfMemory,
_ => panic!("failed to create buffer: {}", err),
})?
};
if !desc.label.is_null() {
unsafe {
let label = ffi::CStr::from_ptr(desc.label).to_string_lossy();
@ -423,16 +434,18 @@ impl<B: GfxBackend> Device<B> {
};
}
let requirements = unsafe { self.raw.get_buffer_requirements(&buffer) };
let memory = self
.mem_allocator
.lock()
.allocate(&self.raw, &requirements, mem_usage, memory_kind)
.unwrap();
let memory =
self.mem_allocator
.lock()
.allocate(&self.raw, &requirements, mem_usage, memory_kind)?;
unsafe {
self.raw
.bind_buffer_memory(memory.memory(), memory.segment().offset, &mut buffer)
.unwrap()
.map_err(|err| match err {
hal::device::BindError::OutOfMemory(_) => CreateBufferError::OutOfMemory,
_ => panic!("failed to bind buffer memory: {}", err),
})?;
};
Ok(resource::Buffer {
@ -501,7 +514,10 @@ impl<B: GfxBackend> Device<B> {
usage,
view_capabilities,
)
.unwrap();
.map_err(|err| match err {
hal::image::CreationError::OutOfMemory(_) => CreateTextureError::OutOfMemory,
_ => panic!("failed to create texture: {}", err),
})?;
if !desc.label.is_null() {
let label = ffi::CStr::from_ptr(desc.label).to_string_lossy();
self.raw.set_image_name(&mut image, &label);
@ -510,21 +526,20 @@ impl<B: GfxBackend> Device<B> {
};
let requirements = unsafe { self.raw.get_image_requirements(&image) };
let memory = self
.mem_allocator
.lock()
.allocate(
&self.raw,
&requirements,
gfx_memory::MemoryUsage::Private,
gfx_memory::Kind::General,
)
.unwrap();
let memory = self.mem_allocator.lock().allocate(
&self.raw,
&requirements,
gfx_memory::MemoryUsage::Private,
gfx_memory::Kind::General,
)?;
unsafe {
self.raw
.bind_image_memory(memory.memory(), memory.segment().offset, &mut image)
.unwrap()
.map_err(|err| match err {
hal::device::BindError::OutOfMemory(_) => CreateTextureError::OutOfMemory,
_ => panic!("failed to bind texture memory: {}", err),
})?;
};
Ok(resource::Texture {
@ -552,7 +567,10 @@ impl<B: GfxBackend> Device<B> {
/// This functions doesn't consider the following aspects for compatibility:
/// - image layouts
/// - resolve attachments
fn create_compatible_render_pass(&self, key: &RenderPassKey) -> B::RenderPass {
fn create_compatible_render_pass(
&self,
key: &RenderPassKey,
) -> Result<B::RenderPass, hal::device::OutOfMemory> {
let mut color_ids = [(0, hal::image::Layout::ColorAttachmentOptimal); MAX_COLOR_TARGETS];
for i in 0..key.colors.len() {
color_ids[i].0 = i;
@ -573,11 +591,7 @@ impl<B: GfxBackend> Device<B> {
};
let all = key.all().map(|(at, _)| at);
unsafe {
self.raw
.create_render_pass(all, iter::once(subpass), &[])
.unwrap()
}
unsafe { self.raw.create_render_pass(all, iter::once(subpass), &[]) }
}
}
@ -684,8 +698,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
&mut buffer,
hal::buffer::SubRange::WHOLE,
HostMap::Write,
)
.expect("failed to map buffer on creation");
)?;
buffer.map_state = resource::BufferMapState::Active {
ptr,
@ -709,7 +722,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let ptr = stage
.memory
.map(&device.raw, hal::memory::Segment::ALL)
.unwrap()
.map_err(BufferAccessError::from)?
.ptr();
buffer.map_state = resource::BufferMapState::Init {
ptr,
@ -781,7 +794,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
buffer_id: id::BufferId,
offset: BufferAddress,
data: &[u8],
) -> Result<(), BufferMapError> {
) -> Result<(), BufferAccessError> {
span!(_guard, INFO, "Device::set_buffer_sub_data");
let hub = B::hub(self);
@ -823,7 +836,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
ptr::copy_nonoverlapping(data.as_ptr(), ptr.as_ptr(), data.len());
}
unmap_buffer(&device.raw, buffer);
unmap_buffer(&device.raw, buffer)?;
Ok(())
}
@ -834,7 +847,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
buffer_id: id::BufferId,
offset: BufferAddress,
data: &mut [u8],
) {
) -> Result<(), BufferAccessError> {
span!(_guard, INFO, "Device::get_buffer_sub_data");
let hub = B::hub(self);
@ -844,14 +857,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let (mut buffer_guard, _) = hub.buffers.write(&mut token);
let device = &device_guard[device_id];
let mut buffer = &mut buffer_guard[buffer_id];
assert!(
buffer.usage.contains(wgt::BufferUsage::MAP_READ),
"Buffer usage {:?} must contain usage flag MAP_READ",
buffer.usage
);
check_buffer_usage(buffer.usage, wgt::BufferUsage::MAP_READ)?;
//assert!(buffer isn't used by the GPU);
match map_buffer(
let ptr = map_buffer(
&device.raw,
&mut buffer,
hal::buffer::SubRange {
@ -859,17 +868,15 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
size: Some(data.len() as BufferAddress),
},
HostMap::Read,
) {
Ok(ptr) => unsafe {
ptr::copy_nonoverlapping(ptr.as_ptr(), data.as_mut_ptr(), data.len());
},
Err(e) => {
log::error!("failed to map a buffer: {:?}", e);
return;
}
)?;
unsafe {
ptr::copy_nonoverlapping(ptr.as_ptr(), data.as_mut_ptr(), data.len());
}
unmap_buffer(&device.raw, buffer);
unmap_buffer(&device.raw, buffer)?;
Ok(())
}
pub fn buffer_destroy<B: GfxBackend>(&self, buffer_id: id::BufferId) {
@ -959,7 +966,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
texture_id: id::TextureId,
desc: Option<&wgt::TextureViewDescriptor<Label>>,
id_in: Input<G, id::TextureViewId>,
) -> id::TextureViewId {
) -> Result<id::TextureViewId, CreateTextureViewError> {
span!(_guard, INFO, "Texture::create_view");
let hub = B::hub(self);
@ -1012,7 +1019,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
hal::format::Swizzle::NO,
range.clone(),
)
.unwrap()
.or(Err(CreateTextureViewError::OutOfMemory))?
};
let view = resource::TextureView {
@ -1048,10 +1055,13 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.views
.init(id, ref_count, PhantomData)
.unwrap();
id
Ok(id)
}
pub fn texture_view_destroy<B: GfxBackend>(&self, texture_view_id: id::TextureViewId) {
pub fn texture_view_destroy<B: GfxBackend>(
&self,
texture_view_id: id::TextureViewId,
) -> Result<(), TextureViewDestroyError> {
span!(_guard, INFO, "Texture::view_destroy");
let hub = B::hub(self);
@ -1068,7 +1078,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
texture_guard[source_id.value].device_id.value
}
resource::TextureViewInner::SwapChain { .. } => {
panic!("Can't destroy a swap chain image")
return Err(TextureViewDestroyError::SwapChainImage)
}
}
};
@ -1079,6 +1089,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.suspected_resources
.texture_views
.push(texture_view_id);
Ok(())
}
pub fn device_create_sampler<B: GfxBackend>(
@ -1086,7 +1097,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
device_id: id::DeviceId,
desc: &wgt::SamplerDescriptor<Label>,
id_in: Input<G, id::SamplerId>,
) -> id::SamplerId {
) -> Result<id::SamplerId, CreateSamplerError> {
span!(_guard, INFO, "Device::create_sampler");
let hub = B::hub(self);
@ -1096,10 +1107,9 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let actual_clamp = if let Some(clamp) = desc.anisotropy_clamp {
let valid_clamp = clamp <= MAX_ANISOTROPY && conv::is_power_of_two(clamp as u32);
assert!(
valid_clamp,
"Anisotropic clamp must be one of the values: 1, 2, 4, 8, or 16"
);
if !valid_clamp {
return Err(CreateSamplerError::InvalidClamp(clamp));
}
if device.private_features.anisotropic_filtering {
Some(clamp)
} else {
@ -1126,8 +1136,14 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
anisotropy_clamp: actual_clamp,
};
let raw = unsafe {
device.raw.create_sampler(&info).map_err(|err| match err {
hal::device::AllocationError::OutOfMemory(_) => CreateSamplerError::OutOfMemory,
hal::device::AllocationError::TooManyObjects => CreateSamplerError::TooManyObjects,
})?
};
let sampler = resource::Sampler {
raw: unsafe { device.raw.create_sampler(&info).unwrap() },
raw,
device_id: Stored {
value: device_id,
ref_count: device.life_guard.add_ref(),
@ -1152,7 +1168,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.samplers
.init(id, ref_count, PhantomData)
.unwrap();
id
Ok(id)
}
pub fn sampler_destroy<B: GfxBackend>(&self, sampler_id: id::SamplerId) {
@ -1181,7 +1197,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
device_id: id::DeviceId,
desc: &wgt::BindGroupLayoutDescriptor,
id_in: Input<G, id::BindGroupLayoutId>,
) -> Result<id::BindGroupLayoutId, binding_model::BindGroupLayoutError> {
) -> Result<id::BindGroupLayoutId, binding_model::CreateBindGroupLayoutError> {
span!(_guard, INFO, "Device::create_bind_group_layout");
let mut token = Token::root();
@ -1189,7 +1205,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let mut entry_map = FastHashMap::default();
for entry in desc.entries.iter() {
if entry_map.insert(entry.binding, entry.clone()).is_some() {
return Err(binding_model::BindGroupLayoutError::ConflictBinding(
return Err(binding_model::CreateBindGroupLayoutError::ConflictBinding(
entry.binding,
));
}
@ -1220,7 +1236,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
{
if let Some(count) = binding.count {
if count == 0 {
return Err(binding_model::BindGroupLayoutError::ZeroCount);
return Err(binding_model::CreateBindGroupLayoutError::ZeroCount);
}
match binding.ty {
wgt::BindingType::SampledTexture { .. } => {
@ -1228,12 +1244,12 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.features
.contains(wgt::Features::SAMPLED_TEXTURE_BINDING_ARRAY)
{
return Err(binding_model::BindGroupLayoutError::MissingFeature(
return Err(binding_model::CreateBindGroupLayoutError::MissingFeature(
wgt::Features::SAMPLED_TEXTURE_BINDING_ARRAY,
));
}
}
_ => return Err(binding_model::BindGroupLayoutError::ArrayUnsupported),
_ => return Err(binding_model::CreateBindGroupLayoutError::ArrayUnsupported),
}
} else {
unreachable!() // programming bug
@ -1258,7 +1274,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let mut raw_layout = device
.raw
.create_descriptor_set_layout(&raw_bindings, &[])
.unwrap();
.or(Err(binding_model::CreateBindGroupLayoutError::OutOfMemory))?;
if let Some(label) = desc.label.as_ref() {
device
.raw
@ -1275,7 +1291,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
// going to violate limits too, lets catch it now.
count_validator
.validate(&device.limits)
.map_err(binding_model::BindGroupLayoutError::TooManyBindings)?;
.map_err(binding_model::CreateBindGroupLayoutError::TooManyBindings)?;
let layout = binding_model::BindGroupLayout {
raw,
@ -1335,7 +1351,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
device_id: id::DeviceId,
desc: &wgt::PipelineLayoutDescriptor<id::BindGroupLayoutId>,
id_in: Input<G, id::PipelineLayoutId>,
) -> Result<id::PipelineLayoutId, PipelineLayoutError> {
) -> Result<id::PipelineLayoutId, CreatePipelineLayoutError> {
span!(_guard, INFO, "Device::create_pipeline_layout");
let hub = B::hub(self);
@ -1346,7 +1362,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let bind_group_layouts_count = desc.bind_group_layouts.len();
let device_max_bind_groups = device.limits.max_bind_groups as usize;
if bind_group_layouts_count > device_max_bind_groups {
return Err(PipelineLayoutError::TooManyGroups {
return Err(CreatePipelineLayoutError::TooManyGroups {
actual: bind_group_layouts_count,
max: device_max_bind_groups,
});
@ -1355,24 +1371,26 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
if !desc.push_constant_ranges.is_empty()
&& !device.features.contains(wgt::Features::PUSH_CONSTANTS)
{
return Err(PipelineLayoutError::MissingFeature(
return Err(CreatePipelineLayoutError::MissingFeature(
wgt::Features::PUSH_CONSTANTS,
));
}
let mut used_stages = wgt::ShaderStage::empty();
for (index, pc) in desc.push_constant_ranges.iter().enumerate() {
if pc.stages.intersects(used_stages) {
return Err(PipelineLayoutError::MoreThanOnePushConstantRangePerStage {
index,
provided: pc.stages,
intersected: pc.stages & used_stages,
});
return Err(
CreatePipelineLayoutError::MoreThanOnePushConstantRangePerStage {
index,
provided: pc.stages,
intersected: pc.stages & used_stages,
},
);
}
used_stages |= pc.stages;
let device_max_pc_size = device.limits.max_push_constant_size;
if device_max_pc_size < pc.range.end {
return Err(PipelineLayoutError::PushConstantRangeTooLarge {
return Err(CreatePipelineLayoutError::PushConstantRangeTooLarge {
index,
range: pc.range.clone(),
max: device_max_pc_size,
@ -1380,13 +1398,13 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
if pc.range.start % wgt::PUSH_CONSTANT_ALIGNMENT != 0 {
return Err(PipelineLayoutError::MisalignedPushConstantRange {
return Err(CreatePipelineLayoutError::MisalignedPushConstantRange {
index,
bound: pc.range.start,
});
}
if pc.range.end % wgt::PUSH_CONSTANT_ALIGNMENT != 0 {
return Err(PipelineLayoutError::MisalignedPushConstantRange {
return Err(CreatePipelineLayoutError::MisalignedPushConstantRange {
index,
bound: pc.range.end,
});
@ -1412,12 +1430,12 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
device
.raw
.create_pipeline_layout(descriptor_set_layouts, push_constants)
.or(Err(binding_model::CreatePipelineLayoutError::OutOfMemory))?
}
.unwrap()
};
count_validator
.validate(&device.limits)
.map_err(PipelineLayoutError::TooManyBindings)?;
.map_err(CreatePipelineLayoutError::TooManyBindings)?;
let layout = binding_model::PipelineLayout {
raw: pipeline_layout,
@ -1515,7 +1533,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
1,
&mut desc_sets,
)
.unwrap();
.expect("failed to allocate descriptor set");
desc_sets.pop().unwrap()
};
@ -1588,32 +1606,24 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
};
assert_eq!(
bb.offset % wgt::BIND_BUFFER_ALIGNMENT,
0,
"Buffer offset {} must be a multiple of BIND_BUFFER_ALIGNMENT",
bb.offset
);
if bb.offset % wgt::BIND_BUFFER_ALIGNMENT != 0 {
return Err(CreateBindGroupError::UnalignedBufferOffset(bb.offset));
}
let buffer = used
.buffers
.use_extend(&*buffer_guard, bb.buffer_id, (), internal_use)
.unwrap();
assert!(
buffer.usage.contains(pub_usage),
"Buffer usage {:?} must contain usage flag(s) {:?}",
buffer.usage,
pub_usage
);
check_buffer_usage(buffer.usage, pub_usage)?;
let (bind_size, bind_end) = match bb.size {
Some(size) => {
let end = bb.offset + size.get();
assert!(
end <= buffer.size,
"Bound buffer range {:?} does not fit in buffer size {}",
bb.offset..end,
buffer.size
);
if end > buffer.size {
return Err(CreateBindGroupError::BindingRangeTooLarge {
range: bb.offset..end,
size: buffer.size,
});
}
(size.get(), end)
}
None => (buffer.size - bb.offset, buffer.size),
@ -1632,13 +1642,14 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
});
}
match min_size {
Some(non_zero) if non_zero.get() > bind_size => panic!(
"Minimum buffer binding size {} is not respected with size {}",
non_zero.get(),
bind_size
),
_ => (),
if let Some(non_zero) = min_size {
let min_size = non_zero.get();
if min_size > bind_size {
return Err(CreateBindGroupError::BindingSizeTooSmall {
actual: bind_size,
min: min_size,
});
}
}
let sub_range = hal::buffer::SubRange {
@ -1711,39 +1722,33 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
internal_use,
)
.unwrap();
assert!(
texture.usage.contains(pub_usage),
"Texture usage {:?} must contain usage flag(s) {:?}",
texture.usage,
pub_usage
);
check_texture_usage(texture.usage, pub_usage)?;
let image_layout =
conv::map_texture_state(internal_use, view.range.aspects).1;
SmallVec::from([hal::pso::Descriptor::Image(raw, image_layout)])
}
resource::TextureViewInner::SwapChain { .. } => {
panic!("Unable to create a bind group with a swap chain image")
return Err(CreateBindGroupError::SwapChainImage);
}
}
}
Br::TextureViewArray(ref bindings_array) => {
assert!(
device.features.contains(wgt::Features::SAMPLED_TEXTURE_BINDING_ARRAY),
"Feature SAMPLED_TEXTURE_BINDING_ARRAY must be enabled to use TextureViewArrays in a bind group"
);
let required_feats = wgt::Features::SAMPLED_TEXTURE_BINDING_ARRAY;
if !device.features.contains(required_feats) {
return Err(CreateBindGroupError::MissingFeatures(required_feats));
}
if let Some(count) = decl.count {
assert_eq!(
count as usize,
bindings_array.len(),
"Binding count declared with {} items, but {} items were provided",
count,
bindings_array.len()
);
let count = count as usize;
let num_bindings = bindings_array.len();
if count != num_bindings {
return Err(CreateBindGroupError::BindingArrayLengthMismatch {
actual: num_bindings,
expected: count,
});
}
} else {
panic!(
"Binding declared as a single item, but bind group is using it as an array",
);
return Err(CreateBindGroupError::SingleBindingExpected);
}
let (pub_usage, internal_use) = match decl.ty {
@ -1781,25 +1786,20 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
internal_use,
)
.unwrap();
assert!(
texture.usage.contains(pub_usage),
"Texture usage {:?} must contain usage flag(s) {:?}",
texture.usage,
pub_usage
);
check_texture_usage(texture.usage, pub_usage)?;
let image_layout = conv::map_texture_state(
internal_use,
view.range.aspects,
)
.1;
hal::pso::Descriptor::Image(raw, image_layout)
Ok(hal::pso::Descriptor::Image(raw, image_layout))
}
resource::TextureViewInner::SwapChain { .. } => {
return Err(CreateBindGroupError::SwapChainImage)
}
resource::TextureViewInner::SwapChain { .. } => panic!(
"Unable to create a bind group with a swap chain image"
),
}
})
.collect()
.collect::<Result<_, _>>()?
}
};
writes.alloc().init(hal::pso::DescriptorSetWrite {
@ -1909,6 +1909,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
naga::front::spv::Parser::new(spv_iter)
.parse()
.map_err(|err| {
// TODO: eventually, when Naga gets support for all features,
// we want to convert these to a hard error,
log::warn!("Failed to parse shader SPIR-V code: {:?}", err);
log::warn!("Shader module will not be validated");
})
@ -1919,6 +1921,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
(spv, module)
}
pipeline::ShaderModuleSource::Wgsl(code) => {
// TODO: refactor the corresponding Naga error to be owned, and then
// display it instead of unwrapping
let module = naga::front::wgsl::parse_str(&code).unwrap();
let spv = naga::back::spv::Writer::new(&module.header, spv_flags).write(&module);
(
@ -1947,8 +1951,19 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
naga::proc::Validator::new().validate(module)?;
}
let raw = unsafe {
device
.raw
.create_shader_module(&spv)
.map_err(|err| match err {
hal::device::ShaderError::OutOfMemory(_) => {
CreateShaderModuleError::OutOfMemory
}
_ => panic!("failed to create shader module: {}", err),
})?
};
let shader = pipeline::ShaderModule {
raw: unsafe { device.raw.create_shader_module(&spv).unwrap() },
raw,
device_id: Stored {
value: device_id,
ref_count: device.life_guard.add_ref(),
@ -2363,7 +2378,9 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
main_pass: match render_pass_cache.entry(rp_key) {
Entry::Occupied(e) => e.into_mut(),
Entry::Vacant(e) => {
let pass = device.create_compatible_render_pass(e.key());
let pass = device
.create_compatible_render_pass(e.key())
.or(Err(pipeline::RenderPipelineError::OutOfMemory))?;
e.insert(pass)
}
},
@ -2376,7 +2393,12 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
device
.raw
.create_graphics_pipeline(&pipeline_desc, None)
.unwrap()
.map_err(|err| match err {
hal::pso::CreationError::OutOfMemory(_) => {
pipeline::RenderPipelineError::OutOfMemory
}
_ => panic!("failed to create graphics pipeline: {}", err),
})?
};
(pipeline, layout.life_guard.add_ref())
@ -2523,12 +2545,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
parent,
};
let pipeline = unsafe {
device
.raw
.create_compute_pipeline(&pipeline_desc, None)
.unwrap()
};
let pipeline = unsafe { device.raw.create_compute_pipeline(&pipeline_desc, None)? };
(pipeline, layout.life_guard.add_ref())
};
@ -2590,7 +2607,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
device_id: id::DeviceId,
surface_id: id::SurfaceId,
desc: &wgt::SwapChainDescriptor,
) -> id::SwapChainId {
) -> Result<id::SwapChainId, CreateSwapChainError> {
span!(_guard, INFO, "Device::create_swap_chain");
fn validate_swap_chain_descriptor(
@ -2633,16 +2650,14 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let surface = &mut surface_guard[surface_id];
let (caps, formats) = {
let suf = B::get_surface_mut(surface);
let surface = B::get_surface_mut(surface);
let adapter = &adapter_guard[device.adapter_id.value];
assert!(
suf.supports_queue_family(&adapter.raw.queue_families[0]),
"Surface {:?} doesn't support queue family {:?}",
suf,
&adapter.raw.queue_families[0]
);
let formats = suf.supported_formats(&adapter.raw.physical_device);
let caps = suf.capabilities(&adapter.raw.physical_device);
let queue_family = &adapter.raw.queue_families[0];
if !surface.supports_queue_family(queue_family) {
return Err(CreateSwapChainError::UnsupportedQueueFamily);
}
let formats = surface.supported_formats(&adapter.raw.physical_device);
let caps = surface.capabilities(&adapter.raw.physical_device);
(caps, formats)
};
let num_frames = swap_chain::DESIRED_NUM_FRAMES
@ -2651,27 +2666,30 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let mut config =
swap_chain::swap_chain_descriptor_to_hal(&desc, num_frames, device.private_features);
if let Some(formats) = formats {
assert!(
formats.contains(&config.format),
"Requested format {:?} is not in supported list: {:?}",
config.format,
formats
);
if !formats.contains(&config.format) {
return Err(CreateSwapChainError::UnsupportedFormat {
requested: config.format,
available: formats,
});
}
}
validate_swap_chain_descriptor(&mut config, &caps);
unsafe {
B::get_surface_mut(surface)
.configure_swapchain(&device.raw, config)
.unwrap();
.map_err(|err| match err {
hal::window::CreationError::OutOfMemory(_) => CreateSwapChainError::OutOfMemory,
hal::window::CreationError::DeviceLost(_) => CreateSwapChainError::DeviceLost,
_ => panic!("failed to configure swap chain on creation: {}", err),
})?;
}
let sc_id = surface_id.to_swap_chain_id(B::VARIANT);
if let Some(sc) = swap_chain_guard.remove(sc_id) {
assert!(
sc.acquired_view_id.is_none(),
"SwapChainOutput must be dropped before a new SwapChain is made."
);
if !sc.acquired_view_id.is_none() {
return Err(CreateSwapChainError::SwapChainOutputExists);
}
unsafe {
device.raw.destroy_semaphore(sc.semaphore);
}
@ -2692,13 +2710,16 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
},
desc: desc.clone(),
num_frames,
semaphore: device.raw.create_semaphore().unwrap(),
semaphore: device
.raw
.create_semaphore()
.or(Err(CreateSwapChainError::OutOfMemory))?,
acquired_view_id: None,
acquired_framebuffers: Vec::new(),
active_submission_index: 0,
};
swap_chain_guard.insert(sc_id, swap_chain);
sc_id
Ok(sc_id)
}
#[cfg(feature = "replay")]
@ -2801,7 +2822,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
buffer_id: id::BufferId,
range: Range<BufferAddress>,
op: resource::BufferMapOperation,
) -> Result<(), BufferMapError> {
) -> Result<(), BufferAccessError> {
span!(_guard, INFO, "Device::buffer_map_async");
let hub = B::hub(self);
@ -2815,7 +2836,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
if range.start % wgt::COPY_BUFFER_ALIGNMENT != 0
|| range.end % wgt::COPY_BUFFER_ALIGNMENT != 0
{
return Err(BufferMapError::UnalignedRange);
return Err(BufferAccessError::UnalignedRange);
}
let (device_id, ref_count) = {
@ -2825,7 +2846,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
check_buffer_usage(buffer.usage, pub_usage)?;
buffer.map_state = match buffer.map_state {
resource::BufferMapState::Init { .. } | resource::BufferMapState::Active { .. } => {
return Err(BufferMapError::AlreadyMapped);
return Err(BufferAccessError::AlreadyMapped);
}
resource::BufferMapState::Waiting(_) => {
op.call_error();
@ -2864,7 +2885,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
buffer_id: id::BufferId,
offset: BufferAddress,
_size: Option<BufferSize>,
) -> Result<*mut u8, BufferNotMappedError> {
) -> Result<*mut u8, BufferAccessError> {
span!(_guard, INFO, "Device::buffer_get_mapped_range");
let hub = B::hub(self);
@ -2878,7 +2899,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
Ok(ptr.as_ptr().offset(offset as isize))
},
resource::BufferMapState::Idle | resource::BufferMapState::Waiting(_) => {
Err(BufferNotMappedError)
Err(BufferAccessError::NotMapped)
}
}
}
@ -2886,7 +2907,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
pub fn buffer_unmap<B: GfxBackend>(
&self,
buffer_id: id::BufferId,
) -> Result<(), BufferNotMappedError> {
) -> Result<(), BufferAccessError> {
span!(_guard, INFO, "Device::buffer_unmap");
let hub = B::hub(self);
@ -2956,7 +2977,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.consume_temp(stage_buffer, stage_memory);
}
resource::BufferMapState::Idle => {
return Err(BufferNotMappedError);
return Err(BufferAccessError::NotMapped);
}
resource::BufferMapState::Waiting(_) => {}
resource::BufferMapState::Active {
@ -2984,7 +3005,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
};
let _ = (ptr, sub_range);
}
unmap_buffer(&device.raw, buffer);
unmap_buffer(&device.raw, buffer)?;
}
}
Ok(())
@ -2993,10 +3014,16 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
#[derive(Clone, Debug, Error)]
pub enum CreateBufferError {
#[error("`MAP` usage can only be combined with the opposite `COPY`, requested {0:?}")]
UsageMismatch(wgt::BufferUsage),
#[error("failed to map buffer while creating: {0}")]
AccessError(#[from] BufferAccessError),
#[error(transparent)]
HeapsError(#[from] gfx_memory::HeapsError),
#[error("not enough memory left")]
OutOfMemory,
#[error("buffers that are mapped at creation have to be aligned to `COPY_BUFFER_ALIGNMENT`")]
UnalignedSize,
#[error("`MAP` usage can only be combined with the opposite `COPY`, requested {0:?}")]
UsageMismatch(wgt::BufferUsage),
}
#[derive(Clone, Debug, Error)]
@ -3008,26 +3035,78 @@ pub enum CreateTextureError {
MAX_MIP_LEVELS
)]
InvalidMipLevelCount(u32),
#[error(transparent)]
HeapsError(#[from] gfx_memory::HeapsError),
#[error("not enough memory left")]
OutOfMemory,
}
#[derive(Clone, Debug, Error)]
pub enum BufferMapError {
#[error(transparent)]
MissingBufferUsage(#[from] MissingBufferUsageError),
#[error(transparent)]
MapError(#[from] hal::device::MapError),
#[error("buffer map range is not aligned to {}", wgt::COPY_BUFFER_ALIGNMENT)]
UnalignedRange,
pub enum BufferAccessError {
#[error("buffer is already mapped")]
AlreadyMapped,
#[error(transparent)]
MissingBufferUsage(#[from] MissingBufferUsageError),
#[error("buffer is not mapped")]
NotMapped,
#[error("not enough memory left")]
OutOfMemory,
#[error("buffer map range is not aligned to {}", wgt::COPY_BUFFER_ALIGNMENT)]
UnalignedRange,
}
impl From<hal::device::MapError> for BufferAccessError {
fn from(error: hal::device::MapError) -> Self {
match error {
hal::device::MapError::OutOfMemory(_) => Self::OutOfMemory,
_ => panic!("failed to map buffer: {}", error),
}
}
}
#[derive(Clone, Debug, Error)]
#[error("buffer is not mapped")]
pub struct BufferNotMappedError;
pub enum CreateSamplerError {
#[error("invalid anisotropic clamp {0}, must be one of 1, 2, 4, 8 or 16")]
InvalidClamp(u8),
#[error("not enough memory left")]
OutOfMemory,
#[error("cannot create any more samplers")]
TooManyObjects,
}
#[derive(Clone, Debug, Error)]
pub enum CreateShaderModuleError {
#[error("not enough memory left")]
OutOfMemory,
#[error(transparent)]
Validation(#[from] naga::proc::ValidationError),
}
#[derive(Clone, Debug, Error)]
pub enum CreateSwapChainError {
#[error("device has been lost")]
DeviceLost,
#[error("not enough memory left")]
OutOfMemory,
#[error("`SwapChainOutput` must be dropped before a new `SwapChain` is made")]
SwapChainOutputExists,
#[error("surface does not support the adapter's queue family")]
UnsupportedQueueFamily,
#[error("requested format {requested:?} is not in list of supported formats: {available:?}")]
UnsupportedFormat {
requested: hal::format::Format,
available: Vec<hal::format::Format>,
},
}
#[derive(Clone, Debug, Error)]
pub enum CreateTextureViewError {
#[error("not enough memory left")]
OutOfMemory,
}
#[derive(Clone, Debug, Error)]
pub enum TextureViewDestroyError {
#[error("cannot destroy swap chain image")]
SwapChainImage,
}

View File

@ -448,7 +448,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
if !buffer.life_guard.use_at(submit_index) {
if let BufferMapState::Active { .. } = buffer.map_state {
log::warn!("Dropped buffer has a pending mapping.");
super::unmap_buffer(&device.raw, buffer);
super::unmap_buffer(&device.raw, buffer).unwrap();
}
device.temp_suspected.buffers.push(id);
} else {

View File

@ -9,6 +9,7 @@ use crate::{
LifeGuard, RefCount, Stored,
};
use std::borrow::{Borrow, Cow};
use thiserror::Error;
use wgt::{BufferAddress, IndexFormat, InputStepMode};
#[repr(C)]
@ -31,9 +32,12 @@ pub type ProgrammableStageDescriptor<'a> = wgt::ProgrammableStageDescriptor<'a,
pub type ComputePipelineDescriptor<'a> =
wgt::ComputePipelineDescriptor<PipelineLayoutId, ProgrammableStageDescriptor<'a>>;
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Error)]
pub enum ComputePipelineError {
#[error(transparent)]
Stage(StageError),
#[error(transparent)]
HalCreationError(#[from] hal::pso::CreationError),
}
#[derive(Debug)]
@ -53,21 +57,26 @@ impl<B: hal::Backend> Borrow<RefCount> for ComputePipeline<B> {
pub type RenderPipelineDescriptor<'a> =
wgt::RenderPipelineDescriptor<'a, PipelineLayoutId, ProgrammableStageDescriptor<'a>>;
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Error)]
pub enum RenderPipelineError {
MissingFeature(wgt::Features),
#[error("incompatible output format at index {index}")]
IncompatibleOutputFormat { index: u8 },
#[error("invalid sample count {0}")]
InvalidSampleCount(u32),
#[error("vertex attribute at location {location} has invalid offset {offset}")]
InvalidVertexAttributeOffset {
location: wgt::ShaderLocation,
offset: BufferAddress,
},
#[error("missing required device features {0:?}")]
MissingFeature(wgt::Features),
#[error("not enough memory left")]
OutOfMemory,
#[error("error in stage {flag:?}: {error}")]
Stage {
flag: wgt::ShaderStage,
error: StageError,
},
IncompatibleOutputFormat {
index: u8,
},
InvalidSampleCount(u32),
}
bitflags::bitflags! {