Initial implementation of GPUBindGroupLayout for WebGPU

Added WebIDL bindings for `GPUBindGroupLayout`, `GPUBindGroupLayoutDescriptor`, `GPUBindingType`, `GPUShaderStage`
and `GPUBindGroupLayoutBinding` (Note: The servo's codegen doesn't like the name, because its already occupied).
Implemented the `createBindGroupLayout` function of `GPUDevice`.
This commit is contained in:
Istvan Miklos 2020-01-06 14:04:38 +01:00
parent 95614f57f1
commit 9cf007472b
13 changed files with 397 additions and 11 deletions

25
Cargo.lock generated
View file

@ -3904,7 +3904,25 @@ version = "0.2.0"
source = "git+https://github.com/servo/webrender#edac864107cf43732ec66a9d3288e869a70ce1de"
dependencies = [
"euclid",
"peek-poke-derive",
"peek-poke-derive 0.2.1",
]
[[package]]
name = "peek-poke"
version = "0.2.0"
source = "git+https://github.com/kvark/peek-poke?rev=969bd7fe2be1a83f87916dc8b388c63cfd457075#969bd7fe2be1a83f87916dc8b388c63cfd457075"
dependencies = [
"peek-poke-derive 0.2.0",
]
[[package]]
name = "peek-poke-derive"
version = "0.2.0"
source = "git+https://github.com/kvark/peek-poke?rev=969bd7fe2be1a83f87916dc8b388c63cfd457075#969bd7fe2be1a83f87916dc8b388c63cfd457075"
dependencies = [
"proc-macro2 1.0.1",
"quote 1.0.2",
"syn 1.0.3",
]
[[package]]
@ -6456,7 +6474,7 @@ dependencies = [
"derive_more 0.13.0",
"euclid",
"malloc_size_of_derive",
"peek-poke",
"peek-poke 0.2.0 (git+https://github.com/servo/webrender)",
"serde",
"serde_bytes",
"serde_derive",
@ -6545,7 +6563,7 @@ dependencies = [
[[package]]
name = "wgpu-core"
version = "0.1.0"
source = "git+https://github.com/gfx-rs/wgpu#c0fa61a064c3572ee60d6c4c6a59ca0571394200"
source = "git+https://github.com/gfx-rs/wgpu#881222a9477036e9e3504045452d88abfe5ae177"
dependencies = [
"arrayvec 0.5.1",
"battery",
@ -6560,6 +6578,7 @@ dependencies = [
"gfx-hal",
"log",
"parking_lot",
"peek-poke 0.2.0 (git+https://github.com/kvark/peek-poke?rev=969bd7fe2be1a83f87916dc8b388c63cfd457075)",
"rendy-descriptor",
"rendy-memory",
"serde",

View file

@ -151,7 +151,7 @@ use tendril::stream::LossyDecoder;
use tendril::{StrTendril, TendrilSink};
use time::{Duration, Timespec, Tm};
use uuid::Uuid;
use webgpu::{WebGPU, WebGPUAdapter, WebGPUBuffer, WebGPUDevice};
use webgpu::{WebGPU, WebGPUAdapter, WebGPUBindGroupLayout, WebGPUBuffer, WebGPUDevice};
use webrender_api::{DocumentId, ImageKey};
use webvr_traits::{WebVRGamepadData, WebVRGamepadHand, WebVRGamepadState};
use webxr_api::SwapChainId as WebXRSwapChainId;
@ -530,6 +530,7 @@ unsafe_no_jsmanaged_fields!(WebGPU);
unsafe_no_jsmanaged_fields!(WebGPUAdapter);
unsafe_no_jsmanaged_fields!(WebGPUDevice);
unsafe_no_jsmanaged_fields!(WebGPUBuffer);
unsafe_no_jsmanaged_fields!(WebGPUBindGroupLayout);
unsafe_no_jsmanaged_fields!(GPUBufferState);
unsafe_no_jsmanaged_fields!(WebXRSwapChainId);
unsafe_no_jsmanaged_fields!(MediaList);

View file

@ -0,0 +1,75 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::GPUBindGroupLayoutBinding::{
self, GPUBindGroupLayoutBindings, GPUBindGroupLayoutMethods,
};
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::globalscope::GlobalScope;
use dom_struct::dom_struct;
use std::cell::Cell;
use webgpu::{WebGPU, WebGPUBindGroupLayout};
#[dom_struct]
pub struct GPUBindGroupLayout {
reflector_: Reflector,
label: DomRefCell<Option<DOMString>>,
bind_group_layout: WebGPUBindGroupLayout,
#[ignore_malloc_size_of = "defined in webgpu"]
bindings: Vec<GPUBindGroupLayoutBindings>,
#[ignore_malloc_size_of = "defined in webgpu"]
channel: WebGPU,
valid: Cell<bool>,
}
impl GPUBindGroupLayout {
fn new_inherited(
channel: WebGPU,
bind_group_layout: WebGPUBindGroupLayout,
bindings: Vec<GPUBindGroupLayoutBindings>,
valid: bool,
) -> GPUBindGroupLayout {
Self {
reflector_: Reflector::new(),
channel,
label: DomRefCell::new(None),
bind_group_layout,
bindings,
valid: Cell::new(valid),
}
}
pub fn new(
global: &GlobalScope,
channel: WebGPU,
bind_group_layout: WebGPUBindGroupLayout,
bindings: Vec<GPUBindGroupLayoutBindings>,
valid: bool,
) -> DomRoot<GPUBindGroupLayout> {
reflect_dom_object(
Box::new(GPUBindGroupLayout::new_inherited(
channel,
bind_group_layout,
bindings,
valid,
)),
global,
GPUBindGroupLayoutBinding::Wrap,
)
}
}
impl GPUBindGroupLayoutMethods for GPUBindGroupLayout {
/// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label
fn GetLabel(&self) -> Option<DOMString> {
self.label.borrow().clone()
}
/// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label
fn SetLabel(&self, value: Option<DOMString>) {
*self.label.borrow_mut() = value;
}
}

View file

@ -5,6 +5,10 @@
#![allow(unsafe_code)]
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::GPUAdapterBinding::GPULimits;
use crate::dom::bindings::codegen::Bindings::GPUBindGroupLayoutBinding::{
GPUBindGroupLayoutBindings, GPUBindGroupLayoutDescriptor, GPUBindingType,
};
use crate::dom::bindings::codegen::Bindings::GPUBufferBinding::GPUBufferDescriptor;
use crate::dom::bindings::codegen::Bindings::GPUDeviceBinding::{self, GPUDeviceMethods};
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods;
@ -15,6 +19,7 @@ use crate::dom::bindings::str::DOMString;
use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
use crate::dom::gpuadapter::GPUAdapter;
use crate::dom::gpubindgrouplayout::GPUBindGroupLayout;
use crate::dom::gpubuffer::{GPUBuffer, GPUBufferState};
use crate::dom::window::Window;
use crate::script_runtime::JSContext as SafeJSContext;
@ -23,7 +28,9 @@ use ipc_channel::ipc;
use js::jsapi::{Heap, JSObject};
use js::jsval::{JSVal, ObjectValue, UndefinedValue};
use js::typedarray::{ArrayBuffer, CreateWith};
use std::collections::{HashMap, HashSet};
use std::ptr::{self, NonNull};
use webgpu::wgpu::binding_model::{BindGroupLayoutBinding, BindingType, ShaderStage};
use webgpu::wgpu::resource::{BufferDescriptor, BufferUsage};
use webgpu::{WebGPU, WebGPUBuffer, WebGPUDevice, WebGPURequest};
@ -229,4 +236,177 @@ impl GPUDeviceMethods for GPUDevice {
self.resolve_create_buffer_mapped(cx, buffer, array_buffer, wgpu_descriptor, valid)
}
}
/// https://gpuweb.github.io/gpuweb/#GPUDevice-createBindGroupLayout
fn CreateBindGroupLayout(
&self,
descriptor: &GPUBindGroupLayoutDescriptor,
) -> DomRoot<GPUBindGroupLayout> {
#[derive(Clone)]
struct MaxLimits {
max_uniform_buffers_per_shader_stage: i32,
max_storage_buffers_per_shader_stage: i32,
max_sampled_textures_per_shader_stage: i32,
max_storage_textures_per_shader_stage: i32,
max_samplers_per_shader_stage: i32,
}
let mut storeBindings = HashSet::new();
// TODO: We should have these limits on device creation
let limits = GPULimits::empty();
let mut validation_map = HashMap::new();
let maxLimits = MaxLimits {
max_uniform_buffers_per_shader_stage: limits.maxUniformBuffersPerShaderStage as i32,
max_storage_buffers_per_shader_stage: limits.maxStorageBuffersPerShaderStage as i32,
max_sampled_textures_per_shader_stage: limits.maxSampledTexturesPerShaderStage as i32,
max_storage_textures_per_shader_stage: limits.maxStorageTexturesPerShaderStage as i32,
max_samplers_per_shader_stage: limits.maxSamplersPerShaderStage as i32,
};
validation_map.insert(
webgpu::wgpu::binding_model::ShaderStage::VERTEX,
maxLimits.clone(),
);
validation_map.insert(
webgpu::wgpu::binding_model::ShaderStage::FRAGMENT,
maxLimits.clone(),
);
validation_map.insert(
webgpu::wgpu::binding_model::ShaderStage::COMPUTE,
maxLimits.clone(),
);
let mut max_dynamic_uniform_buffers_per_pipeline_layout =
limits.maxDynamicUniformBuffersPerPipelineLayout as i32;
let mut max_dynamic_storage_buffers_per_pipeline_layout =
limits.maxDynamicStorageBuffersPerPipelineLayout as i32;
let mut valid = true;
let bindings = descriptor
.bindings
.iter()
.map(|bind| {
// TODO: binding must be >= 0
storeBindings.insert(bind.binding);
let visibility = match ShaderStage::from_bits(bind.visibility) {
Some(visibility) => visibility,
None => {
valid = false;
ShaderStage::from_bits(0).unwrap()
},
};
let ty = match bind.type_ {
GPUBindingType::Uniform_buffer => {
if let Some(limit) = validation_map.get_mut(&visibility) {
limit.max_uniform_buffers_per_shader_stage -= 1;
}
if bind.hasDynamicOffset {
max_dynamic_uniform_buffers_per_pipeline_layout -= 1;
};
BindingType::UniformBuffer
},
GPUBindingType::Storage_buffer => {
if let Some(limit) = validation_map.get_mut(&visibility) {
limit.max_storage_buffers_per_shader_stage -= 1;
}
if bind.hasDynamicOffset {
max_dynamic_storage_buffers_per_pipeline_layout -= 1;
};
BindingType::StorageBuffer
},
GPUBindingType::Readonly_storage_buffer => {
if let Some(limit) = validation_map.get_mut(&visibility) {
limit.max_storage_buffers_per_shader_stage -= 1;
}
if bind.hasDynamicOffset {
max_dynamic_storage_buffers_per_pipeline_layout -= 1;
};
BindingType::ReadonlyStorageBuffer
},
GPUBindingType::Sampled_texture => {
if let Some(limit) = validation_map.get_mut(&visibility) {
limit.max_sampled_textures_per_shader_stage -= 1;
}
if bind.hasDynamicOffset {
valid = false
};
BindingType::SampledTexture
},
GPUBindingType::Storage_texture => {
if let Some(limit) = validation_map.get_mut(&visibility) {
limit.max_storage_textures_per_shader_stage -= 1;
}
if bind.hasDynamicOffset {
valid = false
};
BindingType::StorageTexture
},
GPUBindingType::Sampler => {
if let Some(limit) = validation_map.get_mut(&visibility) {
limit.max_samplers_per_shader_stage -= 1;
}
if bind.hasDynamicOffset {
valid = false
};
BindingType::Sampler
},
};
BindGroupLayoutBinding {
binding: bind.binding,
visibility,
ty,
dynamic: bind.hasDynamicOffset,
multisampled: bind.multisampled,
texture_dimension: webgpu::wgpu::resource::TextureViewDimension::D2, // Use as default for now
}
})
.collect::<Vec<BindGroupLayoutBinding>>();
// bindings are unique
valid &= storeBindings.len() == bindings.len();
// Ensure that values do not exceed the max limit for each ShaderStage.
valid &= validation_map.values().all(|stage| {
stage.max_uniform_buffers_per_shader_stage >= 0 &&
stage.max_storage_buffers_per_shader_stage >= 0 &&
stage.max_sampled_textures_per_shader_stage >= 0 &&
stage.max_storage_textures_per_shader_stage >= 0 &&
stage.max_samplers_per_shader_stage >= 0
});
// DynamicValues does not exceed the max limit for the pipeline
valid &= max_dynamic_uniform_buffers_per_pipeline_layout >= 0 &&
max_dynamic_storage_buffers_per_pipeline_layout >= 0;
let (sender, receiver) = ipc::channel().unwrap();
if let Some(window) = self.global().downcast::<Window>() {
let id = window
.Navigator()
.create_bind_group_layout_id(self.device.0.backend());
self.channel
.0
.send(WebGPURequest::CreateBindGroupLayout(
sender,
self.device,
id,
bindings.clone(),
))
.expect("Failed to create WebGPU BindGroupLayout");
}
let bgl = receiver.recv().unwrap();
let binds = descriptor
.bindings
.iter()
.map(|bind| GPUBindGroupLayoutBindings {
binding: bind.binding,
hasDynamicOffset: bind.hasDynamicOffset,
multisampled: bind.multisampled,
type_: bind.type_,
visibility: bind.visibility,
//texture_dimension: bind.texture_dimension
})
.collect::<Vec<_>>();
GPUBindGroupLayout::new(&self.global(), self.channel.clone(), bgl, binds, valid)
}
}

View file

@ -0,0 +1,11 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use crate::dom::bindings::reflector::Reflector;
use dom_struct::dom_struct;
#[dom_struct]
pub struct GPUShaderStage {
reflector_: Reflector,
}

View file

@ -5,7 +5,7 @@
use smallvec::SmallVec;
use webgpu::wgpu::{
hub::IdentityManager,
id::{AdapterId, BufferId, DeviceId},
id::{AdapterId, BindGroupLayoutId, BufferId, DeviceId},
Backend,
};
@ -14,6 +14,7 @@ pub struct IdentityHub {
adapters: IdentityManager,
devices: IdentityManager,
buffers: IdentityManager,
bind_group_layouts: IdentityManager,
backend: Backend,
}
@ -23,6 +24,7 @@ impl IdentityHub {
adapters: IdentityManager::default(),
devices: IdentityManager::default(),
buffers: IdentityManager::default(),
bind_group_layouts: IdentityManager::default(),
backend,
}
}
@ -35,9 +37,13 @@ impl IdentityHub {
self.devices.alloc(self.backend)
}
pub fn create_buffer_id(&mut self) -> BufferId {
fn create_buffer_id(&mut self) -> BufferId {
self.buffers.alloc(self.backend)
}
fn create_bind_group_layout_id(&mut self) -> BindGroupLayoutId {
self.bind_group_layouts.alloc(self.backend)
}
}
#[derive(Debug)]
@ -119,4 +125,18 @@ impl Identities {
_ => self.dummy_hub.create_buffer_id(),
}
}
pub fn create_bind_group_layout_id(&mut self, backend: Backend) -> BindGroupLayoutId {
match backend {
#[cfg(any(target_os = "linux", target_os = "windows"))]
Backend::Vulkan => self.vk_hub.create_bind_group_layout_id(),
#[cfg(target_os = "windows")]
Backend::Dx12 => self.dx12_hub.create_bind_group_layout_id(),
#[cfg(target_os = "windows")]
Backend::Dx11 => self.dx11_hub.create_bind_group_layout_id(),
#[cfg(any(target_os = "ios", target_os = "macos"))]
Backend::Metal => self.metal_hub.create_bind_group_layout_id(),
_ => self.dummy_hub.create_bind_group_layout_id(),
}
}
}

View file

@ -318,9 +318,11 @@ pub mod gamepadlist;
pub mod globalscope;
pub mod gpu;
pub mod gpuadapter;
pub mod gpubindgrouplayout;
pub mod gpubuffer;
pub mod gpubufferusage;
pub mod gpudevice;
pub mod gpushaderstage;
pub mod hashchangeevent;
pub mod headers;
pub mod history;

View file

@ -28,7 +28,7 @@ use smallvec::SmallVec;
use std::cell::RefCell;
use std::rc::Rc;
use webgpu::wgpu::{
id::{AdapterId, BufferId, DeviceId},
id::{AdapterId, BindGroupLayoutId, BufferId, DeviceId},
Backend,
};
@ -88,6 +88,12 @@ impl Navigator {
pub fn create_buffer_id(&self, backend: Backend) -> BufferId {
self.gpu_id_hub.borrow_mut().create_buffer_id(backend)
}
pub fn create_bind_group_layout_id(&self, backend: Backend) -> BindGroupLayoutId {
self.gpu_id_hub
.borrow_mut()
.create_bind_group_layout_id(backend)
}
}
impl NavigatorMethods for Navigator {

View file

@ -0,0 +1,34 @@
/* 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 https://mozilla.org/MPL/2.0/. */
// https://gpuweb.github.io/gpuweb/#gpubindgrouplayout
[Exposed=(Window, DedicatedWorker), Serializable, Pref="dom.webgpu.enabled"]
interface GPUBindGroupLayout {
};
GPUBindGroupLayout includes GPUObjectBase;
dictionary GPUBindGroupLayoutDescriptor : GPUObjectDescriptorBase {
required sequence<GPUBindGroupLayoutBindings> bindings;
};
// Note: Servo codegen doesn't like the name `GPUBindGroupLayoutBinding` because it's already occupied
// dictionary GPUBindGroupLayoutBinding {
dictionary GPUBindGroupLayoutBindings {
required unsigned long binding;
required GPUShaderStageFlags visibility;
required GPUBindingType type;
//GPUTextureViewDimension textureDimension = "2d";
//GPUTextureComponentType textureComponentType = "float";
boolean multisampled = false;
boolean hasDynamicOffset = false;
};
enum GPUBindingType {
"uniform-buffer",
"storage-buffer",
"readonly-storage-buffer",
"sampler",
"sampled-texture",
"storage-texture"
};

View file

@ -11,12 +11,12 @@ interface GPUDevice : EventTarget {
GPUBuffer createBuffer(GPUBufferDescriptor descriptor);
GPUMappedBuffer createBufferMapped(GPUBufferDescriptor descriptor);
/*Promise<GPUMappedBuffer> createBufferMappedAsync(GPUBufferDescriptor descriptor);
GPUTexture createTexture(GPUTextureDescriptor descriptor);
GPUSampler createSampler(optional GPUSamplerDescriptor descriptor = {});
//Promise<GPUMappedBuffer> createBufferMappedAsync(GPUBufferDescriptor descriptor);
//GPUTexture createTexture(GPUTextureDescriptor descriptor);
//GPUSampler createSampler(optional GPUSamplerDescriptor descriptor = {});
GPUBindGroupLayout createBindGroupLayout(GPUBindGroupLayoutDescriptor descriptor);
GPUPipelineLayout createPipelineLayout(GPUPipelineLayoutDescriptor descriptor);
/*GPUPipelineLayout createPipelineLayout(GPUPipelineLayoutDescriptor descriptor);
GPUBindGroup createBindGroup(GPUBindGroupDescriptor descriptor);
GPUShaderModule createShaderModule(GPUShaderModuleDescriptor descriptor);

View file

@ -0,0 +1,13 @@
/* 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 https://mozilla.org/MPL/2.0/. */
// https://gpuweb.github.io/gpuweb/#typedefdef-gpushaderstageflags
[Exposed=(Window, DedicatedWorker), Serializable, Pref="dom.webgpu.enabled"]
interface GPUShaderStage {
const GPUShaderStageFlags VERTEX = 0x1;
const GPUShaderStageFlags FRAGMENT = 0x2;
const GPUShaderStageFlags COMPUTE = 0x4;
};
typedef unsigned long GPUShaderStageFlags;

View file

@ -48,6 +48,12 @@ pub enum WebGPURequest {
wgpu::id::BufferId,
wgpu::resource::BufferDescriptor,
),
CreateBindGroupLayout(
IpcSender<WebGPUBindGroupLayout>,
WebGPUDevice,
wgpu::id::BindGroupLayoutId,
Vec<wgpu::binding_model::BindGroupLayoutBinding>,
),
UnmapBuffer(WebGPUBuffer),
DestroyBuffer(WebGPUBuffer),
}
@ -220,6 +226,22 @@ impl WGPU {
let global = &self.global;
gfx_select!(buffer.0 => global.buffer_destroy(buffer.0));
},
WebGPURequest::CreateBindGroupLayout(sender, device, id, bindings) => {
let global = &self.global;
let descriptor = wgpu_core::binding_model::BindGroupLayoutDescriptor {
bindings: bindings.as_ptr(),
bindings_length: bindings.len(),
};
let bgl_id = gfx_select!(id => global.device_create_bind_group_layout(device.0, &descriptor, id));
let bgl = WebGPUBindGroupLayout(bgl_id);
if let Err(e) = sender.send(bgl) {
warn!(
"Failed to send response to WebGPURequest::CreateBufferMapped ({})",
e
)
}
},
WebGPURequest::Exit(sender) => {
self.deinit();
if let Err(e) = sender.send(()) {
@ -250,3 +272,4 @@ macro_rules! webgpu_resource {
webgpu_resource!(WebGPUAdapter, wgpu::id::AdapterId);
webgpu_resource!(WebGPUDevice, wgpu::id::DeviceId);
webgpu_resource!(WebGPUBuffer, wgpu::id::BufferId);
webgpu_resource!(WebGPUBindGroupLayout, wgpu::id::BindGroupLayoutId);

View file

@ -33,6 +33,8 @@ packages = [
"gleam",
"mach",
"nix",
"peek-poke",
"peek-poke-derive",
"wayland-sys",
# https://github.com/servo/servo/pull/23288#issuecomment-494687746