diff --git a/Cargo.lock b/Cargo.lock index 8415b74f3f..47eaac07d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,6 +15,12 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" +[[package]] +name = "ahash" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6789e291be47ace86a60303502173d84af8327e3627ecf334356ee0f87a164c" + [[package]] name = "aho-corasick" version = "0.7.15" @@ -690,18 +696,6 @@ dependencies = [ "x11", ] -[[package]] -name = "gfx-descriptor" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd8c7afcd000f279d541a490e27117e61037537279b9342279abf4938fe60c6b" -dependencies = [ - "arrayvec", - "fxhash", - "gfx-hal", - "log", -] - [[package]] name = "gfx-hal" version = "0.6.0" @@ -778,11 +772,32 @@ dependencies = [ "bitflags", ] +[[package]] +name = "gpu-descriptor" +version = "0.1.0" +source = "git+https://github.com/zakarumych/gpu-descriptor?rev=831460c4b5120d9a74744d542f39a95b9816b5ab#831460c4b5120d9a74744d542f39a95b9816b5ab" +dependencies = [ + "bitflags", + "gpu-descriptor-types", + "hashbrown", +] + +[[package]] +name = "gpu-descriptor-types" +version = "0.1.0" +source = "git+https://github.com/zakarumych/gpu-descriptor?rev=831460c4b5120d9a74744d542f39a95b9816b5ab#831460c4b5120d9a74744d542f39a95b9816b5ab" +dependencies = [ + "bitflags", +] + [[package]] name = "hashbrown" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" +dependencies = [ + "ahash", +] [[package]] name = "hermit-abi" @@ -2198,9 +2213,9 @@ dependencies = [ "gfx-backend-gl", "gfx-backend-metal", "gfx-backend-vulkan", - "gfx-descriptor", "gfx-hal", "gpu-alloc", + "gpu-descriptor", "loom", "naga", "parking_lot 0.11.0", diff --git a/README.md b/README.md index 731c776514..15f05f9a28 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ This is an active GitHub mirror of the WebGPU implementation in Rust, which now [![Matrix](https://img.shields.io/badge/Dev_Matrix-%23wgpu%3Amatrix.org-blueviolet.svg)](https://matrix.to/#/#wgpu:matrix.org) [![Matrix](https://img.shields.io/badge/User_Matrix-%23wgpu--users%3Amatrix.org-blueviolet.svg)](https://matrix.to/#/#wgpu-users:matrix.org) [![Build Status](https://github.com/gfx-rs/wgpu/workflows/CI/badge.svg)](https://github.com/gfx-rs/wgpu/actions) -This is the core logic of an experimental [WebGPU](https://www.w3.org/community/gpu/) implementation. It's written in Rust and is based on [gfx-hal](https://github.com/gfx-rs/gfx) with help of [gfx-extras](https://github.com/gfx-rs/gfx-extras). See the upstream [WebGPU specification](https://gpuweb.github.io/gpuweb/) (work in progress). +This is the core logic of an experimental [WebGPU](https://www.w3.org/community/gpu/) implementation. It's written in Rust and is based on [gfx-hal](https://github.com/gfx-rs/gfx) with help of [gpu-alloc](https://github.com/zakarumych/gpu-alloc) and [gpu-descriptor](https://github.com/zakarumych/gpu-descriptor). See the upstream [WebGPU specification](https://gpuweb.github.io/gpuweb/) (work in progress). The implementation consists of the following parts: diff --git a/wgpu-core/Cargo.toml b/wgpu-core/Cargo.toml index 38b14df41f..1e591f3045 100644 --- a/wgpu-core/Cargo.toml +++ b/wgpu-core/Cargo.toml @@ -34,8 +34,8 @@ serde = { version = "1.0", features = ["serde_derive"], optional = true } smallvec = "1" tracing = { version = "0.1", default-features = false, features = ["std"] } thiserror = "1" -gfx-descriptor = "0.2" gpu-alloc = { git = "https://github.com/zakarumych/gpu-alloc", rev = "d07be73f9439a37c89f5b72f2500cbf0eb4ff613" } +gpu-descriptor = { git = "https://github.com/zakarumych/gpu-descriptor", rev = "831460c4b5120d9a74744d542f39a95b9816b5ab"} [dependencies.naga] version = "0.2" diff --git a/wgpu-core/src/binding_model.rs b/wgpu-core/src/binding_model.rs index 569f3b8b72..10126a9b97 100644 --- a/wgpu-core/src/binding_model.rs +++ b/wgpu-core/src/binding_model.rs @@ -3,7 +3,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::{ - device::{DeviceError, SHADER_STAGE_COUNT}, + device::{ + descriptor::{DescriptorSet, DescriptorTotalCount}, + DeviceError, SHADER_STAGE_COUNT, + }, hub::Resource, id::{BindGroupLayoutId, BufferId, DeviceId, SamplerId, TextureViewId, Valid}, track::{TrackerSet, DUMMY_SELECTOR}, @@ -12,7 +15,6 @@ use crate::{ }; use arrayvec::ArrayVec; -use gfx_descriptor::{DescriptorCounts, DescriptorSet}; #[cfg(feature = "replay")] use serde::Deserialize; @@ -322,7 +324,7 @@ pub struct BindGroupLayout { pub(crate) device_id: Stored, pub(crate) multi_ref_count: MultiRefCount, pub(crate) entries: BindEntryMap, - pub(crate) desc_counts: DescriptorCounts, + pub(crate) desc_count: DescriptorTotalCount, pub(crate) dynamic_count: usize, pub(crate) count_validator: BindingTypeMaxCountValidator, #[cfg(debug_assertions)] diff --git a/wgpu-core/src/device/descriptor.rs b/wgpu-core/src/device/descriptor.rs new file mode 100644 index 0000000000..92b896bd31 --- /dev/null +++ b/wgpu-core/src/device/descriptor.rs @@ -0,0 +1,168 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use super::DeviceError; +use arrayvec::ArrayVec; + +pub use gpu_descriptor::DescriptorTotalCount; + +pub type DescriptorSet = gpu_descriptor::DescriptorSet<::DescriptorSet>; + +#[derive(Debug)] +pub struct DescriptorAllocator( + gpu_descriptor::DescriptorAllocator, +); +struct DescriptorDevice<'a, B: hal::Backend>(&'a B::Device); + +impl DescriptorAllocator { + pub fn new() -> Self { + DescriptorAllocator(unsafe { gpu_descriptor::DescriptorAllocator::new(0) }) + } + + pub fn allocate( + &mut self, + device: &B::Device, + layout: &B::DescriptorSetLayout, + layout_descriptor_count: &DescriptorTotalCount, + count: u32, + ) -> Result>, DeviceError> { + self.0 + .allocate( + &DescriptorDevice::(device), + layout, + gpu_descriptor::DescriptorSetLayoutCreateFlags::empty(), + layout_descriptor_count, + count, + ) + .map_err(|err| { + tracing::warn!("Descriptor set allocation failed: {}", err); + DeviceError::OutOfMemory + }) + } + + pub fn free(&mut self, device: &B::Device, sets: impl IntoIterator>) { + unsafe { self.0.free(&DescriptorDevice::(device), sets) } + } + + pub fn cleanup(&mut self, device: &B::Device) { + self.0.cleanup(&DescriptorDevice::(device)) + } +} + +impl + gpu_descriptor::DescriptorDevice + for DescriptorDevice<'_, B> +{ + unsafe fn create_descriptor_pool( + &self, + descriptor_count: &DescriptorTotalCount, + max_sets: u32, + flags: gpu_descriptor::DescriptorPoolCreateFlags, + ) -> Result { + let mut ranges = ArrayVec::<[_; 7]>::new(); + ranges.push(hal::pso::DescriptorRangeDesc { + ty: hal::pso::DescriptorType::Sampler, + count: descriptor_count.sampler as _, + }); + ranges.push(hal::pso::DescriptorRangeDesc { + ty: hal::pso::DescriptorType::Image { + ty: hal::pso::ImageDescriptorType::Sampled { + with_sampler: false, + }, + }, + count: descriptor_count.sampled_image as _, + }); + ranges.push(hal::pso::DescriptorRangeDesc { + ty: hal::pso::DescriptorType::Image { + ty: hal::pso::ImageDescriptorType::Storage { read_only: false }, + }, + count: descriptor_count.storage_image as _, + }); + ranges.push(hal::pso::DescriptorRangeDesc { + ty: hal::pso::DescriptorType::Buffer { + ty: hal::pso::BufferDescriptorType::Uniform, + format: hal::pso::BufferDescriptorFormat::Structured { + dynamic_offset: false, + }, + }, + count: descriptor_count.uniform_buffer as _, + }); + ranges.push(hal::pso::DescriptorRangeDesc { + ty: hal::pso::DescriptorType::Buffer { + ty: hal::pso::BufferDescriptorType::Storage { read_only: false }, + format: hal::pso::BufferDescriptorFormat::Structured { + dynamic_offset: false, + }, + }, + count: descriptor_count.storage_buffer as _, + }); + ranges.push(hal::pso::DescriptorRangeDesc { + ty: hal::pso::DescriptorType::Buffer { + ty: hal::pso::BufferDescriptorType::Uniform, + format: hal::pso::BufferDescriptorFormat::Structured { + dynamic_offset: true, + }, + }, + count: descriptor_count.uniform_buffer_dynamic as _, + }); + ranges.push(hal::pso::DescriptorRangeDesc { + ty: hal::pso::DescriptorType::Buffer { + ty: hal::pso::BufferDescriptorType::Storage { read_only: false }, + format: hal::pso::BufferDescriptorFormat::Structured { + dynamic_offset: true, + }, + }, + count: descriptor_count.storage_buffer_dynamic as _, + }); + ranges.retain(|rd| rd.count != 0); + + match hal::device::Device::create_descriptor_pool( + self.0, + max_sets as usize, + ranges, + hal::pso::DescriptorPoolCreateFlags::from_bits_truncate(flags.bits() as u32), + ) { + Ok(pool) => Ok(pool), + Err(hal::device::OutOfMemory::Host) => { + Err(gpu_descriptor::CreatePoolError::OutOfHostMemory) + } + Err(hal::device::OutOfMemory::Device) => { + Err(gpu_descriptor::CreatePoolError::OutOfDeviceMemory) + } + } + } + + unsafe fn destroy_descriptor_pool(&self, pool: B::DescriptorPool) { + hal::device::Device::destroy_descriptor_pool(self.0, pool); + } + + unsafe fn alloc_descriptor_sets<'a>( + &self, + pool: &mut B::DescriptorPool, + layouts: impl Iterator, + sets: &mut impl Extend, + ) -> Result<(), gpu_descriptor::DeviceAllocationError> { + use gpu_descriptor::DeviceAllocationError as Dae; + match hal::pso::DescriptorPool::allocate(pool, layouts, sets) { + Ok(()) => Ok(()), + Err(hal::pso::AllocationError::OutOfMemory(oom)) => Err(match oom { + hal::device::OutOfMemory::Host => Dae::OutOfHostMemory, + hal::device::OutOfMemory::Device => Dae::OutOfDeviceMemory, + }), + Err(hal::pso::AllocationError::OutOfPoolMemory) => Err(Dae::OutOfPoolMemory), + Err(hal::pso::AllocationError::FragmentedPool) => Err(Dae::FragmentedPool), + Err(hal::pso::AllocationError::IncompatibleLayout) => { + panic!("Incompatible descriptor set layout") + } + } + } + + unsafe fn dealloc_descriptor_sets<'a>( + &self, + pool: &mut B::DescriptorPool, + sets: impl Iterator, + ) { + hal::pso::DescriptorPool::free(pool, sets) + } +} diff --git a/wgpu-core/src/device/life.rs b/wgpu-core/src/device/life.rs index 4d3c73ac33..3c46b72362 100644 --- a/wgpu-core/src/device/life.rs +++ b/wgpu-core/src/device/life.rs @@ -5,7 +5,12 @@ #[cfg(feature = "trace")] use crate::device::trace; use crate::{ - device::{alloc, queue::TempResource, DeviceError}, + device::{ + alloc, + descriptor::{DescriptorAllocator, DescriptorSet}, + queue::TempResource, + DeviceError, + }, hub::{GfxBackend, GlobalIdentityHandlerFactory, Hub, Token}, id, resource, track::TrackerSet, @@ -13,7 +18,6 @@ use crate::{ }; use copyless::VecHelper as _; -use gfx_descriptor::{DescriptorAllocator, DescriptorSet}; use hal::device::Device as _; use parking_lot::Mutex; use thiserror::Error; @@ -159,7 +163,7 @@ impl NonReferencedResources { if !self.desc_sets.is_empty() { descriptor_allocator_mutex .lock() - .free(self.desc_sets.drain(..)); + .free(device, self.desc_sets.drain(..)); } for raw in self.compute_pipes.drain(..) { diff --git a/wgpu-core/src/device/mod.rs b/wgpu-core/src/device/mod.rs index c464fe4ca4..a632f06e56 100644 --- a/wgpu-core/src/device/mod.rs +++ b/wgpu-core/src/device/mod.rs @@ -17,7 +17,6 @@ use crate::{ use arrayvec::ArrayVec; use copyless::VecHelper as _; -use gfx_descriptor::DescriptorAllocator; use hal::{ command::CommandBuffer as _, device::Device as _, @@ -41,6 +40,7 @@ use std::{ }; pub mod alloc; +pub mod descriptor; mod life; mod queue; #[cfg(any(feature = "trace", feature = "replay"))] @@ -218,7 +218,7 @@ pub struct Device { pub(crate) queue_group: hal::queue::QueueGroup, pub(crate) cmd_allocator: command::CommandAllocator, mem_allocator: Mutex>, - desc_allocator: Mutex>, + desc_allocator: Mutex>, //Note: The submission index here corresponds to the last submission that is done. pub(crate) life_guard: LifeGuard, pub(crate) active_submission_index: SubmissionIndex, @@ -260,7 +260,7 @@ impl Device { .or(Err(CreateDeviceError::OutOfMemory))?; let mem_allocator = alloc::MemoryAllocator::new(mem_props, hal_limits); - let descriptors = unsafe { DescriptorAllocator::new() }; + let descriptors = descriptor::DescriptorAllocator::new(); #[cfg(not(feature = "trace"))] match trace_path { Some(_) => tracing::error!("Feature 'trace' is not enabled"), @@ -985,23 +985,51 @@ impl Device { label: Option<&str>, entry_map: binding_model::BindEntryMap, ) -> Result, binding_model::CreateBindGroupLayoutError> { - // Validate the count parameter + let mut desc_count = descriptor::DescriptorTotalCount::default(); for binding in entry_map.values() { - if binding.count.is_some() { - match binding.ty { - wgt::BindingType::Texture { .. } => { - if !self - .features - .contains(wgt::Features::SAMPLED_TEXTURE_BINDING_ARRAY) - { - return Err(binding_model::CreateBindGroupLayoutError::MissingFeature( - wgt::Features::SAMPLED_TEXTURE_BINDING_ARRAY, - )); - } + use wgt::BindingType as Bt; + let (counter, array_feature) = match binding.ty { + Bt::Buffer { + ty: wgt::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: _, + } => (&mut desc_count.uniform_buffer, None), + Bt::Buffer { + ty: wgt::BufferBindingType::Uniform, + has_dynamic_offset: true, + min_binding_size: _, + } => (&mut desc_count.uniform_buffer_dynamic, None), + Bt::Buffer { + ty: wgt::BufferBindingType::Storage { .. }, + has_dynamic_offset: false, + min_binding_size: _, + } => (&mut desc_count.storage_buffer, None), + Bt::Buffer { + ty: wgt::BufferBindingType::Storage { .. }, + has_dynamic_offset: true, + min_binding_size: _, + } => (&mut desc_count.storage_buffer_dynamic, None), + Bt::Sampler { .. } => (&mut desc_count.sampler, None), + Bt::Texture { .. } => ( + &mut desc_count.sampled_image, + Some(wgt::Features::SAMPLED_TEXTURE_BINDING_ARRAY), + ), + Bt::StorageTexture { .. } => (&mut desc_count.storage_image, None), + }; + *counter += match binding.count { + // Validate the count parameter + Some(count) => { + let feature = array_feature + .ok_or(binding_model::CreateBindGroupLayoutError::ArrayUnsupported)?; + if !self.features.contains(feature) { + return Err(binding_model::CreateBindGroupLayoutError::MissingFeature( + feature, + )); } - _ => return Err(binding_model::CreateBindGroupLayoutError::ArrayUnsupported), + count.get() } - } + None => 1, + }; } let raw_bindings = entry_map @@ -1015,8 +1043,6 @@ impl Device { stage_flags: conv::map_shader_stage_flags(entry.visibility), immutable_samplers: false, // TODO }); - let desc_counts = raw_bindings.clone().collect(); - let raw = unsafe { let mut raw_layout = self .raw @@ -1046,7 +1072,7 @@ impl Device { ref_count: self.life_guard.add_ref(), }, multi_ref_count: MultiRefCount::new(), - desc_counts, + desc_count, dynamic_count: entry_map .values() .filter(|b| b.ty.has_dynamic_offset()) @@ -1343,17 +1369,10 @@ impl Device { } } - let mut desc_sets = ArrayVec::<[_; 1]>::new(); - self.desc_allocator - .lock() - .allocate( - &self.raw, - &layout.raw, - &layout.desc_counts, - 1, - &mut desc_sets, - ) - .expect("failed to allocate descriptor set"); + let mut desc_sets = + self.desc_allocator + .lock() + .allocate(&self.raw, &layout.raw, &layout.desc_count, 1)?; let mut desc_set = desc_sets.pop().unwrap(); // Set the descriptor set's label for easier debugging. @@ -2156,9 +2175,9 @@ impl Device { impl Device { pub(crate) fn destroy_bind_group(&self, bind_group: binding_model::BindGroup) { - unsafe { - self.desc_allocator.lock().free(iter::once(bind_group.raw)); - } + self.desc_allocator + .lock() + .free(&self.raw, iter::once(bind_group.raw)); } pub(crate) fn destroy_buffer(&self, buffer: resource::Buffer) { @@ -2195,7 +2214,7 @@ impl Device { .dispose(&self.raw, &self.cmd_allocator, &mut mem_alloc); self.cmd_allocator.destroy(&self.raw); unsafe { - desc_alloc.clear(&self.raw); + desc_alloc.cleanup(&self.raw); mem_alloc.clear(&self.raw); for (_, rp) in self.render_passes.lock().drain() { self.raw.destroy_render_pass(rp);