diff options
author | Samson <16504129+sagudev@users.noreply.github.com> | 2024-05-08 15:53:39 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-05-08 13:53:39 +0000 |
commit | 168d43f24a18184afadc6aa8646537289c85860a (patch) | |
tree | 138629b4b003141fbb5c6cb8fcf8cee3272664c2 | |
parent | c4f8599404e52da58e9e4e8f8aef3e7efa4996c9 (diff) | |
download | servo-168d43f24a18184afadc6aa8646537289c85860a.tar.gz servo-168d43f24a18184afadc6aa8646537289c85860a.zip |
webgpu: Refactor webgpu crate (#32255)
* wgpu(_core) -> wgc
* Refactor webgpu crate
split lib.rs into multiple modules
21 files changed, 1475 insertions, 1413 deletions
diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index 5948ee302cf..32febf8ab8e 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -56,8 +56,7 @@ use script_traits::{ }; use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl}; use uuid::Uuid; -use webgpu::identity::WebGPUOpResult; -use webgpu::{ErrorScopeId, WebGPUDevice}; +use webgpu::{ErrorScopeId, WebGPUDevice, WebGPUOpResult}; use super::bindings::trace::HashMapTracedValues; use crate::dom::bindings::cell::{DomRefCell, RefMut}; diff --git a/components/script/dom/gpu.rs b/components/script/dom/gpu.rs index d3babad3850..1dbd852f0f3 100644 --- a/components/script/dom/gpu.rs +++ b/components/script/dom/gpu.rs @@ -10,7 +10,7 @@ use ipc_channel::router::ROUTER; use js::jsapi::Heap; use script_traits::ScriptMsg; use webgpu::wgt::PowerPreference; -use webgpu::{wgpu, WebGPUResponse, WebGPUResponseResult}; +use webgpu::{wgc, WebGPUResponse, WebGPUResponseResult}; use super::bindings::codegen::Bindings::WebGPUBinding::GPUTextureFormat; use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{ @@ -117,7 +117,7 @@ impl GPUMethods for GPU { if script_to_constellation_chan .send(ScriptMsg::RequestAdapter( sender, - wgpu::instance::RequestAdapterOptions { + wgc::instance::RequestAdapterOptions { power_preference, compatible_surface: None, force_fallback_adapter: options.forceFallbackAdapter, diff --git a/components/script/dom/gpubuffer.rs b/components/script/dom/gpubuffer.rs index 400bfa3c57d..bb747a998cc 100644 --- a/components/script/dom/gpubuffer.rs +++ b/components/script/dom/gpubuffer.rs @@ -11,9 +11,10 @@ use std::sync::{Arc, Mutex}; use dom_struct::dom_struct; use ipc_channel::ipc::IpcSharedMemory; use js::typedarray::{ArrayBuffer, ArrayBufferU8}; -use webgpu::identity::WebGPUOpResult; -use webgpu::wgpu::device::HostMap; -use webgpu::{WebGPU, WebGPUBuffer, WebGPURequest, WebGPUResponse, WebGPUResponseResult}; +use webgpu::wgc::device::HostMap; +use webgpu::{ + WebGPU, WebGPUBuffer, WebGPUOpResult, WebGPURequest, WebGPUResponse, WebGPUResponseResult, +}; use super::bindings::buffer_source::{create_new_external_array_buffer, HeapBufferSource}; use crate::dom::bindings::cell::DomRefCell; diff --git a/components/script/dom/gpucanvascontext.rs b/components/script/dom/gpucanvascontext.rs index 775b21530ab..0aa45648f8e 100644 --- a/components/script/dom/gpucanvascontext.rs +++ b/components/script/dom/gpucanvascontext.rs @@ -9,7 +9,7 @@ use dom_struct::dom_struct; use euclid::default::Size2D; use ipc_channel::ipc; use script_layout_interface::HTMLCanvasDataSource; -use webgpu::wgpu::id; +use webgpu::wgc::id; use webgpu::{wgt, WebGPU, WebGPURequest, WebGPUTexture, PRESENTATION_BUFFER_COUNT}; use webrender_api::{ units, ExternalImageData, ExternalImageId, ExternalImageType, ImageData, ImageDescriptor, diff --git a/components/script/dom/gpucommandencoder.rs b/components/script/dom/gpucommandencoder.rs index 7bd5de4af94..8f26601b122 100644 --- a/components/script/dom/gpucommandencoder.rs +++ b/components/script/dom/gpucommandencoder.rs @@ -7,7 +7,7 @@ use std::cell::Cell; use std::collections::HashSet; use dom_struct::dom_struct; -use webgpu::wgpu::command as wgpu_com; +use webgpu::wgc::command as wgpu_com; use webgpu::{self, wgt, WebGPU, WebGPURequest}; use crate::dom::bindings::cell::DomRefCell; diff --git a/components/script/dom/gpucomputepassencoder.rs b/components/script/dom/gpucomputepassencoder.rs index 40264947eb5..cb0153b3c22 100644 --- a/components/script/dom/gpucomputepassencoder.rs +++ b/components/script/dom/gpucomputepassencoder.rs @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use dom_struct::dom_struct; -use webgpu::wgpu::command::{compute_commands as wgpu_comp, ComputePass}; +use webgpu::wgc::command::{compute_commands as wgpu_comp, ComputePass}; use webgpu::{WebGPU, WebGPURequest}; use super::bindings::error::Fallible; diff --git a/components/script/dom/gpuconvert.rs b/components/script/dom/gpuconvert.rs index 82b6b1dc2f0..bdfa9b8dd35 100644 --- a/components/script/dom/gpuconvert.rs +++ b/components/script/dom/gpuconvert.rs @@ -4,7 +4,7 @@ use std::borrow::Cow; -use webgpu::wgpu::command as wgpu_com; +use webgpu::wgc::command as wgpu_com; use webgpu::wgt; use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{ diff --git a/components/script/dom/gpudevice.rs b/components/script/dom/gpudevice.rs index b7a705261ef..f208ee37c47 100644 --- a/components/script/dom/gpudevice.rs +++ b/components/script/dom/gpudevice.rs @@ -13,12 +13,11 @@ use std::sync::{Arc, Mutex}; use dom_struct::dom_struct; use js::jsapi::{Heap, JSObject}; -use webgpu::identity::WebGPUOpResult; -use webgpu::wgpu::id::{BindGroupLayoutId, PipelineLayoutId}; -use webgpu::wgpu::{ +use webgpu::wgc::id::{BindGroupLayoutId, PipelineLayoutId}; +use webgpu::wgc::{ binding_model as wgpu_bind, command as wgpu_com, pipeline as wgpu_pipe, resource as wgpu_res, }; -use webgpu::{self, wgt, ErrorScopeId, WebGPU, WebGPURequest}; +use webgpu::{self, wgt, ErrorScopeId, WebGPU, WebGPUOpResult, WebGPURequest}; use super::bindings::codegen::UnionTypes::GPUPipelineLayoutOrGPUAutoLayoutMode; use super::bindings::error::Fallible; diff --git a/components/script/dom/gpuqueue.rs b/components/script/dom/gpuqueue.rs index bcc60eb5dd2..4a2fef174b3 100644 --- a/components/script/dom/gpuqueue.rs +++ b/components/script/dom/gpuqueue.rs @@ -6,8 +6,7 @@ use std::rc::Rc; use dom_struct::dom_struct; use ipc_channel::ipc::IpcSharedMemory; -use webgpu::identity::WebGPUOpResult; -use webgpu::{wgt, WebGPU, WebGPUQueue, WebGPURequest, WebGPUResponse}; +use webgpu::{wgt, WebGPU, WebGPUOpResult, WebGPUQueue, WebGPURequest, WebGPUResponse}; use super::bindings::codegen::Bindings::WebGPUBinding::{GPUImageCopyTexture, GPUImageDataLayout}; use super::gpu::{response_async, AsyncWGPUListener}; diff --git a/components/script/dom/gpurenderbundleencoder.rs b/components/script/dom/gpurenderbundleencoder.rs index 6287b8233d7..7fb3de9c489 100644 --- a/components/script/dom/gpurenderbundleencoder.rs +++ b/components/script/dom/gpurenderbundleencoder.rs @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use dom_struct::dom_struct; -use webgpu::wgpu::command::{bundle_ffi as wgpu_bundle, RenderBundleEncoder}; +use webgpu::wgc::command::{bundle_ffi as wgpu_bundle, RenderBundleEncoder}; use webgpu::{wgt, WebGPU, WebGPURenderBundle, WebGPURequest}; use super::bindings::codegen::Bindings::WebGPUBinding::GPUIndexFormat; diff --git a/components/script/dom/gpurenderpassencoder.rs b/components/script/dom/gpurenderpassencoder.rs index ee3d1621ba3..a0a87ad4f4d 100644 --- a/components/script/dom/gpurenderpassencoder.rs +++ b/components/script/dom/gpurenderpassencoder.rs @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use dom_struct::dom_struct; -use webgpu::wgpu::command::{render_commands as wgpu_render, RenderPass}; +use webgpu::wgc::command::{render_commands as wgpu_render, RenderPass}; use webgpu::{wgt, WebGPU, WebGPURequest}; use super::bindings::codegen::Bindings::WebGPUBinding::GPUIndexFormat; diff --git a/components/script/dom/gputexture.rs b/components/script/dom/gputexture.rs index 0b7db7a16fd..4f97024f774 100644 --- a/components/script/dom/gputexture.rs +++ b/components/script/dom/gputexture.rs @@ -6,9 +6,8 @@ use std::cell::Cell; use std::string::String; use dom_struct::dom_struct; -use webgpu::identity::WebGPUOpResult; -use webgpu::wgpu::resource; -use webgpu::{wgt, WebGPU, WebGPURequest, WebGPUTexture, WebGPUTextureView}; +use webgpu::wgc::resource; +use webgpu::{wgt, WebGPU, WebGPUOpResult, WebGPURequest, WebGPUTexture, WebGPUTextureView}; use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{ diff --git a/components/script/dom/identityhub.rs b/components/script/dom/identityhub.rs index 976637dac51..391afa641c9 100644 --- a/components/script/dom/identityhub.rs +++ b/components/script/dom/identityhub.rs @@ -3,16 +3,16 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use smallvec::SmallVec; -use webgpu::wgpu::id::markers::{ +use webgpu::wgc::id::markers::{ Adapter, BindGroup, BindGroupLayout, Buffer, CommandEncoder, ComputePipeline, Device, PipelineLayout, RenderBundle, RenderPipeline, Sampler, ShaderModule, Texture, TextureView, }; -use webgpu::wgpu::id::{ +use webgpu::wgc::id::{ AdapterId, BindGroupId, BindGroupLayoutId, BufferId, CommandEncoderId, ComputePipelineId, DeviceId, PipelineLayoutId, RenderBundleId, RenderPipelineId, SamplerId, ShaderModuleId, TextureId, TextureViewId, }; -use webgpu::wgpu::identity::IdentityManager; +use webgpu::wgc::identity::IdentityManager; use webgpu::wgt::Backend; #[derive(Debug)] diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 36914a21507..e5af478a5c9 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -95,7 +95,7 @@ use style::dom::OpaqueNode; use style::thread_state::{self, ThreadState}; use time::precise_time_ns; use url::Position; -use webgpu::identity::WebGPUMsg; +use webgpu::WebGPUMsg; use webrender_api::units::LayoutPixel; use webrender_api::DocumentId; diff --git a/components/shared/script/lib.rs b/components/shared/script/lib.rs index 76d0e74bd31..43010955aff 100644 --- a/components/shared/script/lib.rs +++ b/components/shared/script/lib.rs @@ -55,7 +55,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use servo_atoms::Atom; use servo_url::{ImmutableOrigin, ServoUrl}; use style_traits::{CSSPixel, SpeculativePainter}; -use webgpu::identity::WebGPUMsg; +use webgpu::WebGPUMsg; use webrender_api::units::{DeviceIntSize, DevicePixel, DevicePoint, LayoutPixel, LayoutPoint}; use webrender_api::{ BuiltDisplayList, BuiltDisplayListDescriptor, DocumentId, ExternalImageData, ExternalScrollId, diff --git a/components/shared/script/script_msg.rs b/components/shared/script/script_msg.rs index ff854b8d26f..a51859231ba 100644 --- a/components/shared/script/script_msg.rs +++ b/components/shared/script/script_msg.rs @@ -24,7 +24,7 @@ use serde::{Deserialize, Serialize}; use servo_url::{ImmutableOrigin, ServoUrl}; use smallvec::SmallVec; use style_traits::CSSPixel; -use webgpu::{wgpu, WebGPU, WebGPUResponseResult}; +use webgpu::{wgc, WebGPU, WebGPUResponseResult}; use webrender_api::units::{DeviceIntPoint, DeviceIntSize}; use crate::{ @@ -258,8 +258,8 @@ pub enum ScriptMsg { /// Create a WebGPU Adapter instance RequestAdapter( IpcSender<Option<WebGPUResponseResult>>, - wgpu::instance::RequestAdapterOptions, - SmallVec<[wgpu::id::AdapterId; 4]>, + wgc::instance::RequestAdapterOptions, + SmallVec<[wgc::id::AdapterId; 4]>, ), /// Get WebGPU channel GetWebGPUChan(IpcSender<Option<WebGPU>>), diff --git a/components/webgpu/dom_messages.rs b/components/webgpu/dom_messages.rs new file mode 100644 index 00000000000..6edd6f93e88 --- /dev/null +++ b/components/webgpu/dom_messages.rs @@ -0,0 +1,254 @@ +/* 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/. */ + +//! IPC massages that are send/received from GPU DOM objects. + +use std::borrow::Cow; + +use arrayvec::ArrayVec; +use ipc_channel::ipc::{IpcSender, IpcSharedMemory}; +use msg::constellation_msg::PipelineId; +use serde::{Deserialize, Serialize}; +use smallvec::SmallVec; +use webrender_api::{ExternalImageId, ImageData, ImageDescriptor, ImageKey}; +use wgc::binding_model::{ + BindGroupDescriptor, BindGroupLayoutDescriptor, PipelineLayoutDescriptor, +}; +use wgc::command::{ + ComputePass, ImageCopyBuffer, ImageCopyTexture, RenderBundleDescriptor, RenderBundleEncoder, + RenderPass, +}; +use wgc::device::HostMap; +use wgc::id; +use wgc::instance::RequestAdapterOptions; +use wgc::pipeline::{ComputePipelineDescriptor, RenderPipelineDescriptor}; +use wgc::resource::{ + BufferDescriptor, SamplerDescriptor, TextureDescriptor, TextureViewDescriptor, +}; +pub use {wgpu_core as wgc, wgpu_types as wgt}; + +use crate::identity::*; +use crate::{WebGPU, PRESENTATION_BUFFER_COUNT}; + +#[derive(Debug, Deserialize, Serialize)] +#[allow(clippy::large_enum_variant)] +pub enum WebGPUResponse { + RequestAdapter { + adapter_info: wgt::AdapterInfo, + adapter_id: WebGPUAdapter, + features: wgt::Features, + limits: wgt::Limits, + channel: WebGPU, + }, + RequestDevice { + device_id: WebGPUDevice, + queue_id: WebGPUQueue, + descriptor: wgt::DeviceDescriptor<Option<String>>, + }, + BufferMapAsync(IpcSharedMemory), + SubmittedWorkDone, +} + +pub type WebGPUResponseResult = Result<WebGPUResponse, String>; + +#[derive(Debug, Deserialize, Serialize)] +pub enum WebGPURequest { + BufferMapAsync { + sender: IpcSender<Option<WebGPUResponseResult>>, + buffer_id: id::BufferId, + device_id: id::DeviceId, + host_map: HostMap, + offset: u64, + size: Option<u64>, + }, + CommandEncoderFinish { + command_encoder_id: id::CommandEncoderId, + device_id: id::DeviceId, + is_error: bool, + // TODO(zakorgy): Serialize CommandBufferDescriptor in wgpu-core + // wgc::command::CommandBufferDescriptor, + }, + CopyBufferToBuffer { + command_encoder_id: id::CommandEncoderId, + source_id: id::BufferId, + source_offset: wgt::BufferAddress, + destination_id: id::BufferId, + destination_offset: wgt::BufferAddress, + size: wgt::BufferAddress, + }, + CopyBufferToTexture { + command_encoder_id: id::CommandEncoderId, + source: ImageCopyBuffer, + destination: ImageCopyTexture, + copy_size: wgt::Extent3d, + }, + CopyTextureToBuffer { + command_encoder_id: id::CommandEncoderId, + source: ImageCopyTexture, + destination: ImageCopyBuffer, + copy_size: wgt::Extent3d, + }, + CopyTextureToTexture { + command_encoder_id: id::CommandEncoderId, + source: ImageCopyTexture, + destination: ImageCopyTexture, + copy_size: wgt::Extent3d, + }, + CreateBindGroup { + device_id: id::DeviceId, + bind_group_id: id::BindGroupId, + descriptor: BindGroupDescriptor<'static>, + }, + CreateBindGroupLayout { + device_id: id::DeviceId, + bind_group_layout_id: id::BindGroupLayoutId, + descriptor: Option<BindGroupLayoutDescriptor<'static>>, + }, + CreateBuffer { + device_id: id::DeviceId, + buffer_id: id::BufferId, + descriptor: Option<BufferDescriptor<'static>>, + }, + CreateCommandEncoder { + device_id: id::DeviceId, + // TODO(zakorgy): Serialize CommandEncoderDescriptor in wgpu-core + // wgc::command::CommandEncoderDescriptor, + command_encoder_id: id::CommandEncoderId, + label: Option<Cow<'static, str>>, + }, + CreateComputePipeline { + device_id: id::DeviceId, + compute_pipeline_id: id::ComputePipelineId, + descriptor: ComputePipelineDescriptor<'static>, + implicit_ids: Option<(id::PipelineLayoutId, Vec<id::BindGroupLayoutId>)>, + }, + CreateContext(IpcSender<ExternalImageId>), + CreatePipelineLayout { + device_id: id::DeviceId, + pipeline_layout_id: id::PipelineLayoutId, + descriptor: PipelineLayoutDescriptor<'static>, + }, + CreateRenderPipeline { + device_id: id::DeviceId, + render_pipeline_id: id::RenderPipelineId, + descriptor: Option<RenderPipelineDescriptor<'static>>, + implicit_ids: Option<(id::PipelineLayoutId, Vec<id::BindGroupLayoutId>)>, + }, + CreateSampler { + device_id: id::DeviceId, + sampler_id: id::SamplerId, + descriptor: SamplerDescriptor<'static>, + }, + CreateShaderModule { + device_id: id::DeviceId, + program_id: id::ShaderModuleId, + program: String, + label: Option<String>, + }, + CreateSwapChain { + device_id: id::DeviceId, + buffer_ids: ArrayVec<id::BufferId, PRESENTATION_BUFFER_COUNT>, + external_id: u64, + sender: IpcSender<ImageKey>, + image_desc: ImageDescriptor, + image_data: ImageData, + }, + CreateTexture { + device_id: id::DeviceId, + texture_id: id::TextureId, + descriptor: Option<TextureDescriptor<'static>>, + }, + CreateTextureView { + texture_id: id::TextureId, + texture_view_id: id::TextureViewId, + device_id: id::DeviceId, + descriptor: Option<TextureViewDescriptor<'static>>, + }, + DestroyBuffer(id::BufferId), + DestroyDevice(id::DeviceId), + DestroyTexture { + device_id: id::DeviceId, + texture_id: id::TextureId, + }, + DestroySwapChain { + external_id: u64, + image_key: ImageKey, + }, + DropTexture(id::TextureId), + DropAdapter(id::AdapterId), + DropDevice(id::DeviceId), + DropBuffer(id::BufferId), + DropPipelineLayout(id::PipelineLayoutId), + DropComputePipeline(id::ComputePipelineId), + DropRenderPipeline(id::RenderPipelineId), + DropBindGroup(id::BindGroupId), + DropBindGroupLayout(id::BindGroupLayoutId), + DropCommandBuffer(id::CommandBufferId), + DropTextureView(id::TextureViewId), + DropSampler(id::SamplerId), + DropShaderModule(id::ShaderModuleId), + DropRenderBundle(id::RenderBundleId), + DropQuerySet(id::QuerySetId), + Exit(IpcSender<()>), + RenderBundleEncoderFinish { + render_bundle_encoder: RenderBundleEncoder, + descriptor: RenderBundleDescriptor<'static>, + render_bundle_id: id::RenderBundleId, + device_id: id::DeviceId, + }, + RequestAdapter { + sender: IpcSender<Option<WebGPUResponseResult>>, + options: RequestAdapterOptions, + ids: SmallVec<[id::AdapterId; 4]>, + }, + RequestDevice { + sender: IpcSender<Option<WebGPUResponseResult>>, + adapter_id: WebGPUAdapter, + descriptor: wgt::DeviceDescriptor<Option<String>>, + device_id: id::DeviceId, + pipeline_id: PipelineId, + }, + RunComputePass { + command_encoder_id: id::CommandEncoderId, + compute_pass: Option<ComputePass>, + }, + RunRenderPass { + command_encoder_id: id::CommandEncoderId, + render_pass: Option<RenderPass>, + }, + Submit { + queue_id: id::QueueId, + command_buffers: Vec<id::CommandBufferId>, + }, + SwapChainPresent { + external_id: u64, + texture_id: id::TextureId, + encoder_id: id::CommandEncoderId, + }, + UnmapBuffer { + buffer_id: id::BufferId, + device_id: id::DeviceId, + array_buffer: IpcSharedMemory, + is_map_read: bool, + offset: u64, + size: u64, + }, + WriteBuffer { + queue_id: id::QueueId, + buffer_id: id::BufferId, + buffer_offset: u64, + data: IpcSharedMemory, + }, + WriteTexture { + queue_id: id::QueueId, + texture_cv: ImageCopyTexture, + data_layout: wgt::ImageDataLayout, + size: wgt::Extent3d, + data: IpcSharedMemory, + }, + QueueOnSubmittedWorkDone { + sender: IpcSender<Option<WebGPUResponseResult>>, + queue_id: id::QueueId, + }, +} diff --git a/components/webgpu/identity.rs b/components/webgpu/identity.rs index 019428fe9c7..86ea497f67d 100644 --- a/components/webgpu/identity.rs +++ b/components/webgpu/identity.rs @@ -2,51 +2,44 @@ * 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 msg::constellation_msg::PipelineId; +use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use serde::{Deserialize, Serialize}; -use crate::wgpu::id::{ - AdapterId, BindGroupId, BindGroupLayoutId, BufferId, CommandBufferId, ComputePipelineId, - DeviceId, PipelineLayoutId, QuerySetId, RenderBundleId, RenderPipelineId, SamplerId, - ShaderModuleId, StagingBufferId, SurfaceId, TextureId, TextureViewId, +use crate::wgc::id::{ + AdapterId, BindGroupId, BindGroupLayoutId, BufferId, CommandBufferId, CommandEncoderId, + ComputePipelineId, DeviceId, PipelineLayoutId, QueueId, RenderBundleId, RenderPipelineId, + SamplerId, ShaderModuleId, SurfaceId, TextureId, TextureViewId, }; -use crate::{ErrorScopeId, WebGPUDevice}; -#[derive(Clone, Debug, Deserialize, Serialize)] -pub enum WebGPUOpResult { - ValidationError(String), - OutOfMemoryError, - Success, -} +macro_rules! webgpu_resource { + ($name:ident, $id:ty) => { + #[derive(Clone, Copy, Debug, Deserialize, Hash, PartialEq, Serialize)] + pub struct $name(pub $id); + + impl MallocSizeOf for $name { + fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { + 0 + } + } -#[derive(Clone, Debug, Deserialize, Serialize)] -pub enum WebGPUMsg { - FreeAdapter(AdapterId), - FreeDevice(DeviceId), - FreeBuffer(BufferId), - FreePipelineLayout(PipelineLayoutId), - FreeComputePipeline(ComputePipelineId), - FreeRenderPipeline(RenderPipelineId), - FreeBindGroup(BindGroupId), - FreeBindGroupLayout(BindGroupLayoutId), - FreeCommandBuffer(CommandBufferId), - FreeTexture(TextureId), - FreeTextureView(TextureViewId), - FreeSampler(SamplerId), - FreeSurface(SurfaceId), - FreeShaderModule(ShaderModuleId), - FreeRenderBundle(RenderBundleId), - FreeStagingBuffer(StagingBufferId), - FreeQuerySet(QuerySetId), - WebGPUOpResult { - device: WebGPUDevice, - scope_id: Option<ErrorScopeId>, - pipeline_id: PipelineId, - result: WebGPUOpResult, - }, - CleanDevice { - device: WebGPUDevice, - pipeline_id: PipelineId, - }, - Exit, + impl Eq for $name {} + }; } + +webgpu_resource!(WebGPUAdapter, AdapterId); +webgpu_resource!(WebGPUBindGroup, BindGroupId); +webgpu_resource!(WebGPUBindGroupLayout, BindGroupLayoutId); +webgpu_resource!(WebGPUBuffer, BufferId); +webgpu_resource!(WebGPUCommandBuffer, CommandBufferId); +webgpu_resource!(WebGPUCommandEncoder, CommandEncoderId); +webgpu_resource!(WebGPUComputePipeline, ComputePipelineId); +webgpu_resource!(WebGPUDevice, DeviceId); +webgpu_resource!(WebGPUPipelineLayout, PipelineLayoutId); +webgpu_resource!(WebGPUQueue, QueueId); +webgpu_resource!(WebGPURenderBundle, RenderBundleId); +webgpu_resource!(WebGPURenderPipeline, RenderPipelineId); +webgpu_resource!(WebGPUSampler, SamplerId); +webgpu_resource!(WebGPUShaderModule, ShaderModuleId); +webgpu_resource!(WebGPUSurface, SurfaceId); +webgpu_resource!(WebGPUTexture, TextureId); +webgpu_resource!(WebGPUTextureView, TextureViewId); diff --git a/components/webgpu/lib.rs b/components/webgpu/lib.rs index cfe2d9c1689..97bcb137267 100644 --- a/components/webgpu/lib.rs +++ b/components/webgpu/lib.rs @@ -2,278 +2,38 @@ * 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 log::{error, warn}; -use wgpu::device::queue::SubmittedWorkDoneClosure; -use wgpu::gfx_select; -pub use {wgpu_core as wgpu, wgpu_types as wgt}; +use log::warn; +use webrender::RenderApiSender; +use wgpu_thread::WGPU; +pub use {wgpu_core as wgc, wgpu_types as wgt}; pub mod identity; +mod wgpu_thread; use std::borrow::Cow; -use std::cell::RefCell; use std::collections::HashMap; use std::num::NonZeroU64; -use std::slice; use std::sync::{Arc, Mutex}; -use std::time::{Duration, Instant}; use arrayvec::ArrayVec; use euclid::default::Size2D; -use identity::{WebGPUMsg, WebGPUOpResult}; -use ipc_channel::ipc::{self, IpcReceiver, IpcSender, IpcSharedMemory}; -use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; -use msg::constellation_msg::PipelineId; +use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use serde::{Deserialize, Serialize}; use servo_config::pref; -use smallvec::SmallVec; -use webrender::{RenderApi, RenderApiSender, Transaction}; -use webrender_api::{DirtyRect, DocumentId, ExternalImageId, ImageData, ImageDescriptor, ImageKey}; +use webrender_api::{DocumentId, ImageData, ImageDescriptor, ImageKey}; use webrender_traits::{ - WebrenderExternalImageApi, WebrenderExternalImageRegistry, WebrenderImageHandlerType, - WebrenderImageSource, + WebrenderExternalImageApi, WebrenderExternalImageRegistry, WebrenderImageSource, }; -use wgpu::binding_model::{ - BindGroupDescriptor, BindGroupLayoutDescriptor, PipelineLayoutDescriptor, -}; -use wgpu::command::{ - ComputePass, ImageCopyBuffer, ImageCopyTexture, RenderBundleDescriptor, RenderBundleEncoder, - RenderPass, -}; -use wgpu::device::{DeviceDescriptor, HostMap, ImplicitPipelineIds}; -use wgpu::id; -use wgpu::instance::RequestAdapterOptions; -use wgpu::pipeline::{ComputePipelineDescriptor, RenderPipelineDescriptor, ShaderModuleDescriptor}; -use wgpu::resource::{ - BufferDescriptor, BufferMapCallback, BufferMapOperation, SamplerDescriptor, TextureDescriptor, - TextureViewDescriptor, -}; -use wgt::InstanceDescriptor; - -pub type ErrorScopeId = NonZeroU64; -const DEVICE_POLL_INTERVAL: Duration = Duration::from_millis(50); -pub const PRESENTATION_BUFFER_COUNT: usize = 10; - -#[derive(Debug, Deserialize, Serialize)] -#[allow(clippy::large_enum_variant)] -pub enum WebGPUResponse { - RequestAdapter { - adapter_info: wgt::AdapterInfo, - adapter_id: WebGPUAdapter, - features: wgt::Features, - limits: wgt::Limits, - channel: WebGPU, - }, - RequestDevice { - device_id: WebGPUDevice, - queue_id: WebGPUQueue, - descriptor: wgt::DeviceDescriptor<Option<String>>, - }, - BufferMapAsync(IpcSharedMemory), - SubmittedWorkDone, -} +use wgc::id; -pub type WebGPUResponseResult = Result<WebGPUResponse, String>; +mod dom_messages; +mod script_messages; +pub use dom_messages::*; +pub use identity::*; +pub use script_messages::*; -#[derive(Debug, Deserialize, Serialize)] -pub enum WebGPURequest { - BufferMapAsync { - sender: IpcSender<Option<WebGPUResponseResult>>, - buffer_id: id::BufferId, - device_id: id::DeviceId, - host_map: HostMap, - offset: u64, - size: Option<u64>, - }, - CommandEncoderFinish { - command_encoder_id: id::CommandEncoderId, - device_id: id::DeviceId, - is_error: bool, - // TODO(zakorgy): Serialize CommandBufferDescriptor in wgpu-core - // wgpu::command::CommandBufferDescriptor, - }, - CopyBufferToBuffer { - command_encoder_id: id::CommandEncoderId, - source_id: id::BufferId, - source_offset: wgt::BufferAddress, - destination_id: id::BufferId, - destination_offset: wgt::BufferAddress, - size: wgt::BufferAddress, - }, - CopyBufferToTexture { - command_encoder_id: id::CommandEncoderId, - source: ImageCopyBuffer, - destination: ImageCopyTexture, - copy_size: wgt::Extent3d, - }, - CopyTextureToBuffer { - command_encoder_id: id::CommandEncoderId, - source: ImageCopyTexture, - destination: ImageCopyBuffer, - copy_size: wgt::Extent3d, - }, - CopyTextureToTexture { - command_encoder_id: id::CommandEncoderId, - source: ImageCopyTexture, - destination: ImageCopyTexture, - copy_size: wgt::Extent3d, - }, - CreateBindGroup { - device_id: id::DeviceId, - bind_group_id: id::BindGroupId, - descriptor: BindGroupDescriptor<'static>, - }, - CreateBindGroupLayout { - device_id: id::DeviceId, - bind_group_layout_id: id::BindGroupLayoutId, - descriptor: Option<BindGroupLayoutDescriptor<'static>>, - }, - CreateBuffer { - device_id: id::DeviceId, - buffer_id: id::BufferId, - descriptor: Option<BufferDescriptor<'static>>, - }, - CreateCommandEncoder { - device_id: id::DeviceId, - // TODO(zakorgy): Serialize CommandEncoderDescriptor in wgpu-core - // wgpu::command::CommandEncoderDescriptor, - command_encoder_id: id::CommandEncoderId, - label: Option<Cow<'static, str>>, - }, - CreateComputePipeline { - device_id: id::DeviceId, - compute_pipeline_id: id::ComputePipelineId, - descriptor: ComputePipelineDescriptor<'static>, - implicit_ids: Option<(id::PipelineLayoutId, Vec<id::BindGroupLayoutId>)>, - }, - CreateContext(IpcSender<ExternalImageId>), - CreatePipelineLayout { - device_id: id::DeviceId, - pipeline_layout_id: id::PipelineLayoutId, - descriptor: PipelineLayoutDescriptor<'static>, - }, - CreateRenderPipeline { - device_id: id::DeviceId, - render_pipeline_id: id::RenderPipelineId, - descriptor: Option<RenderPipelineDescriptor<'static>>, - implicit_ids: Option<(id::PipelineLayoutId, Vec<id::BindGroupLayoutId>)>, - }, - CreateSampler { - device_id: id::DeviceId, - sampler_id: id::SamplerId, - descriptor: SamplerDescriptor<'static>, - }, - CreateShaderModule { - device_id: id::DeviceId, - program_id: id::ShaderModuleId, - program: String, - label: Option<String>, - }, - CreateSwapChain { - device_id: id::DeviceId, - buffer_ids: ArrayVec<id::BufferId, PRESENTATION_BUFFER_COUNT>, - external_id: u64, - sender: IpcSender<ImageKey>, - image_desc: ImageDescriptor, - image_data: ImageData, - }, - CreateTexture { - device_id: id::DeviceId, - texture_id: id::TextureId, - descriptor: Option<TextureDescriptor<'static>>, - }, - CreateTextureView { - texture_id: id::TextureId, - texture_view_id: id::TextureViewId, - device_id: id::DeviceId, - descriptor: Option<TextureViewDescriptor<'static>>, - }, - DestroyBuffer(id::BufferId), - DestroyDevice(id::DeviceId), - DestroyTexture { - device_id: id::DeviceId, - texture_id: id::TextureId, - }, - DestroySwapChain { - external_id: u64, - image_key: ImageKey, - }, - DropTexture(id::TextureId), - DropAdapter(id::AdapterId), - DropDevice(id::DeviceId), - DropBuffer(id::BufferId), - DropPipelineLayout(id::PipelineLayoutId), - DropComputePipeline(id::ComputePipelineId), - DropRenderPipeline(id::RenderPipelineId), - DropBindGroup(id::BindGroupId), - DropBindGroupLayout(id::BindGroupLayoutId), - DropCommandBuffer(id::CommandBufferId), - DropTextureView(id::TextureViewId), - DropSampler(id::SamplerId), - DropShaderModule(id::ShaderModuleId), - DropRenderBundle(id::RenderBundleId), - DropQuerySet(id::QuerySetId), - Exit(IpcSender<()>), - RenderBundleEncoderFinish { - render_bundle_encoder: RenderBundleEncoder, - descriptor: RenderBundleDescriptor<'static>, - render_bundle_id: id::RenderBundleId, - device_id: id::DeviceId, - }, - RequestAdapter { - sender: IpcSender<Option<WebGPUResponseResult>>, - options: RequestAdapterOptions, - ids: SmallVec<[id::AdapterId; 4]>, - }, - RequestDevice { - sender: IpcSender<Option<WebGPUResponseResult>>, - adapter_id: WebGPUAdapter, - descriptor: wgt::DeviceDescriptor<Option<String>>, - device_id: id::DeviceId, - pipeline_id: PipelineId, - }, - RunComputePass { - command_encoder_id: id::CommandEncoderId, - compute_pass: Option<ComputePass>, - }, - RunRenderPass { - command_encoder_id: id::CommandEncoderId, - render_pass: Option<RenderPass>, - }, - Submit { - queue_id: id::QueueId, - command_buffers: Vec<id::CommandBufferId>, - }, - SwapChainPresent { - external_id: u64, - texture_id: id::TextureId, - encoder_id: id::CommandEncoderId, - }, - UnmapBuffer { - buffer_id: id::BufferId, - device_id: id::DeviceId, - array_buffer: IpcSharedMemory, - is_map_read: bool, - offset: u64, - size: u64, - }, - WriteBuffer { - queue_id: id::QueueId, - buffer_id: id::BufferId, - buffer_offset: u64, - data: IpcSharedMemory, - }, - WriteTexture { - queue_id: id::QueueId, - texture_cv: ImageCopyTexture, - data_layout: wgt::ImageDataLayout, - size: wgt::Extent3d, - data: IpcSharedMemory, - }, - QueueOnSubmittedWorkDone { - sender: IpcSender<Option<WebGPUResponseResult>>, - queue_id: id::QueueId, - }, -} +pub type ErrorScopeId = NonZeroU64; +pub use wgpu_thread::PRESENTATION_BUFFER_COUNT; #[derive(Clone, Debug, Deserialize, Serialize)] pub struct WebGPU(pub IpcSender<(Option<ErrorScopeId>, WebGPURequest)>); @@ -339,1091 +99,6 @@ impl WebGPU { } } -#[allow(clippy::upper_case_acronyms)] // Name of the library -struct WGPU { - receiver: IpcReceiver<(Option<ErrorScopeId>, WebGPURequest)>, - sender: IpcSender<(Option<ErrorScopeId>, WebGPURequest)>, - script_sender: IpcSender<WebGPUMsg>, - global: Arc<wgpu::global::Global>, - adapters: Vec<WebGPUAdapter>, - devices: HashMap<WebGPUDevice, PipelineId>, - // Track invalid adapters https://gpuweb.github.io/gpuweb/#invalid - _invalid_adapters: Vec<WebGPUAdapter>, - //TODO: Remove this (https://github.com/gfx-rs/wgpu/issues/867) - error_command_encoders: RefCell<HashMap<id::CommandEncoderId, String>>, - webrender_api: Arc<Mutex<RenderApi>>, - webrender_document: DocumentId, - external_images: Arc<Mutex<WebrenderExternalImageRegistry>>, - wgpu_image_map: Arc<Mutex<HashMap<u64, PresentationData>>>, - last_poll: Instant, -} - -impl WGPU { - fn new( - receiver: IpcReceiver<(Option<ErrorScopeId>, WebGPURequest)>, - sender: IpcSender<(Option<ErrorScopeId>, WebGPURequest)>, - script_sender: IpcSender<WebGPUMsg>, - webrender_api_sender: RenderApiSender, - webrender_document: DocumentId, - external_images: Arc<Mutex<WebrenderExternalImageRegistry>>, - wgpu_image_map: Arc<Mutex<HashMap<u64, PresentationData>>>, - ) -> Self { - WGPU { - receiver, - sender, - script_sender, - global: Arc::new(wgpu::global::Global::new( - "wgpu-core", - InstanceDescriptor { - backends: wgt::Backends::PRIMARY, - ..Default::default() - }, - )), - adapters: Vec::new(), - devices: HashMap::new(), - _invalid_adapters: Vec::new(), - error_command_encoders: RefCell::new(HashMap::new()), - webrender_api: Arc::new(Mutex::new(webrender_api_sender.create_api())), - webrender_document, - external_images, - wgpu_image_map, - last_poll: Instant::now(), - } - } - - fn run(&mut self) { - loop { - let diff = DEVICE_POLL_INTERVAL.checked_sub(self.last_poll.elapsed()); - if diff.is_none() { - let _ = self.global.poll_all_devices(false); - self.last_poll = Instant::now(); - } - if let Ok((scope_id, msg)) = self - .receiver - .try_recv_timeout(diff.unwrap_or(DEVICE_POLL_INTERVAL)) - { - match msg { - WebGPURequest::BufferMapAsync { - sender, - buffer_id, - device_id, - host_map, - offset, - size, - } => { - let glob = Arc::clone(&self.global); - let resp_sender = sender.clone(); - let callback = BufferMapCallback::from_rust(Box::from(move |result| { - match result { - Ok(()) => { - let global = &glob; - let (slice_pointer, range_size) = gfx_select!(buffer_id => - global.buffer_get_mapped_range(buffer_id, 0, None)) - .unwrap(); - // SAFETY: guarantee to be safe from wgpu - let data = unsafe { - slice::from_raw_parts(slice_pointer, range_size as usize) - }; - - if let Err(e) = - resp_sender.send(Some(Ok(WebGPUResponse::BufferMapAsync( - IpcSharedMemory::from_bytes(data), - )))) - { - warn!("Could not send BufferMapAsync Response ({})", e); - } - }, - Err(_) => { - warn!("Could not map buffer({:?})", buffer_id); - if let Err(e) = resp_sender - .send(Some(Err(String::from("Failed to map Buffer")))) - { - warn!("Could not send BufferMapAsync Response ({})", e); - } - }, - }; - })); - - let operation = BufferMapOperation { - host: host_map, - callback: Some(callback), - }; - let global = &self.global; - let result = gfx_select!(buffer_id => global.buffer_map_async( - buffer_id, - offset, - size, - operation - )); - if let Err(ref e) = result { - if let Err(w) = sender.send(Some(Err(format!("{:?}", e)))) { - warn!("Failed to send BufferMapAsync Response ({:?})", w); - } - } - self.send_result(device_id, scope_id, result); - }, - WebGPURequest::CommandEncoderFinish { - command_encoder_id, - device_id, - is_error, - } => { - let global = &self.global; - let result = if is_error { - Err(String::from("Invalid GPUCommandEncoder")) - } else if let Some(err) = self - .error_command_encoders - .borrow() - .get(&command_encoder_id) - { - Err(err.clone()) - } else { - tuple_to_result( - gfx_select!(command_encoder_id => global.command_encoder_finish( - command_encoder_id, - &wgt::CommandBufferDescriptor::default() - )), - ) - .map_err(|e| format!("{:?}", e)) - }; - self.encoder_record_error(command_encoder_id, &result); - self.send_result(device_id, scope_id, result); - }, - WebGPURequest::CopyBufferToBuffer { - command_encoder_id, - source_id, - source_offset, - destination_id, - destination_offset, - size, - } => { - let global = &self.global; - let result = gfx_select!(command_encoder_id => global.command_encoder_copy_buffer_to_buffer( - command_encoder_id, - source_id, - source_offset, - destination_id, - destination_offset, - size - )); - self.encoder_record_error(command_encoder_id, &result); - }, - WebGPURequest::CopyBufferToTexture { - command_encoder_id, - source, - destination, - copy_size, - } => { - let global = &self.global; - let result = gfx_select!(command_encoder_id => global.command_encoder_copy_buffer_to_texture( - command_encoder_id, - &source, - &destination, - ©_size - )); - self.encoder_record_error(command_encoder_id, &result); - }, - WebGPURequest::CopyTextureToBuffer { - command_encoder_id, - source, - destination, - copy_size, - } => { - let global = &self.global; - let result = gfx_select!(command_encoder_id => global.command_encoder_copy_texture_to_buffer( - command_encoder_id, - &source, - &destination, - ©_size - )); - self.encoder_record_error(command_encoder_id, &result); - }, - WebGPURequest::CopyTextureToTexture { - command_encoder_id, - source, - destination, - copy_size, - } => { - let global = &self.global; - let result = gfx_select!(command_encoder_id => global.command_encoder_copy_texture_to_texture( - command_encoder_id, - &source, - &destination, - ©_size - )); - self.encoder_record_error(command_encoder_id, &result); - }, - WebGPURequest::CreateBindGroup { - device_id, - bind_group_id, - descriptor, - } => { - let global = &self.global; - let result = tuple_to_result(gfx_select!(bind_group_id => - global.device_create_bind_group(device_id, &descriptor, Some(bind_group_id)))); - self.send_result(device_id, scope_id, result); - }, - WebGPURequest::CreateBindGroupLayout { - device_id, - bind_group_layout_id, - descriptor, - } => { - let global = &self.global; - if let Some(desc) = descriptor { - let result = tuple_to_result(gfx_select!(bind_group_layout_id => - global.device_create_bind_group_layout(device_id, &desc, Some(bind_group_layout_id)))); - - self.send_result(device_id, scope_id, result); - } - }, - WebGPURequest::CreateBuffer { - device_id, - buffer_id, - descriptor, - } => { - let global = &self.global; - if let Some(desc) = descriptor { - let result = tuple_to_result(gfx_select!(buffer_id => - global.device_create_buffer(device_id, &desc, Some(buffer_id)))); - - self.send_result(device_id, scope_id, result); - } - }, - WebGPURequest::CreateCommandEncoder { - device_id, - command_encoder_id, - label, - } => { - let global = &self.global; - let desc = wgt::CommandEncoderDescriptor { label }; - let result = tuple_to_result(gfx_select!(command_encoder_id => - global.device_create_command_encoder(device_id, &desc, Some(command_encoder_id)))); - - self.send_result(device_id, scope_id, result); - }, - WebGPURequest::CreateComputePipeline { - device_id, - compute_pipeline_id, - descriptor, - implicit_ids, - } => { - let global = &self.global; - let bgls = implicit_ids - .as_ref() - .map_or(Vec::with_capacity(0), |(_, bgls)| { - bgls.iter().map(|x| Some(x.to_owned())).collect() - }); - let implicit = - implicit_ids - .as_ref() - .map(|(layout, _)| ImplicitPipelineIds { - root_id: Some(*layout), - group_ids: bgls.as_slice(), - }); - let result = tuple_to_result( - gfx_select!(compute_pipeline_id => global.device_create_compute_pipeline( - device_id, - &descriptor, - Some(compute_pipeline_id), - implicit - )), - ); - self.send_result(device_id, scope_id, result); - }, - WebGPURequest::CreateContext(sender) => { - let id = self - .external_images - .lock() - .expect("Lock poisoned?") - .next_id(WebrenderImageHandlerType::WebGPU); - if let Err(e) = sender.send(id) { - warn!("Failed to send ExternalImageId to new context ({})", e); - }; - }, - WebGPURequest::CreatePipelineLayout { - device_id, - pipeline_layout_id, - descriptor, - } => { - let global = &self.global; - let result = tuple_to_result(gfx_select!(pipeline_layout_id => - global.device_create_pipeline_layout(device_id, &descriptor, Some(pipeline_layout_id)))); - self.send_result(device_id, scope_id, result); - }, - WebGPURequest::CreateRenderPipeline { - device_id, - render_pipeline_id, - descriptor, - implicit_ids, - } => { - let global = &self.global; - let bgls = implicit_ids - .as_ref() - .map_or(Vec::with_capacity(0), |(_, bgls)| { - bgls.iter().map(|x| Some(x.to_owned())).collect() - }); - let implicit = - implicit_ids - .as_ref() - .map(|(layout, _)| ImplicitPipelineIds { - root_id: Some(*layout), - group_ids: bgls.as_slice(), - }); - if let Some(desc) = descriptor { - let result = tuple_to_result(gfx_select!(render_pipeline_id => - global.device_create_render_pipeline( - device_id, - &desc, - Some(render_pipeline_id), - implicit) - )); - self.send_result(device_id, scope_id, result); - } - }, - WebGPURequest::CreateSampler { - device_id, - sampler_id, - descriptor, - } => { - let global = &self.global; - let result = tuple_to_result( - gfx_select!(sampler_id => global.device_create_sampler( - device_id, - &descriptor, - Some(sampler_id) - )), - ); - self.send_result(device_id, scope_id, result); - }, - WebGPURequest::CreateShaderModule { - device_id, - program_id, - program, - label, - } => { - let global = &self.global; - let source = wgpu_core::pipeline::ShaderModuleSource::Wgsl( - crate::Cow::Owned(program), - ); - let desc = ShaderModuleDescriptor { - label: label.map(|s| s.into()), - shader_bound_checks: wgt::ShaderBoundChecks::default(), - }; - let result = tuple_to_result(gfx_select!(program_id => - global.device_create_shader_module(device_id, &desc, source, Some(program_id)))); - self.send_result(device_id, scope_id, result); - }, - WebGPURequest::CreateSwapChain { - device_id, - buffer_ids, - external_id, - sender, - image_desc, - image_data, - } => { - let height = image_desc.size.height; - let width = image_desc.size.width; - let buffer_stride = - ((width * 4) as u32 | (wgt::COPY_BYTES_PER_ROW_ALIGNMENT - 1)) + 1; - let mut wr = self.webrender_api.lock().unwrap(); - let image_key = wr.generate_image_key(); - if let Err(e) = sender.send(image_key) { - warn!("Failed to send ImageKey ({})", e); - } - let _ = self.wgpu_image_map.lock().unwrap().insert( - external_id, - PresentationData { - device_id, - queue_id: device_id.transmute(), - data: vec![255; (buffer_stride * height as u32) as usize], - size: Size2D::new(width, height), - unassigned_buffer_ids: buffer_ids, - available_buffer_ids: ArrayVec::< - id::BufferId, - PRESENTATION_BUFFER_COUNT, - >::new(), - queued_buffer_ids: ArrayVec::< - id::BufferId, - PRESENTATION_BUFFER_COUNT, - >::new(), - buffer_stride, - image_key, - image_desc, - image_data: image_data.clone(), - }, - ); - - let mut txn = Transaction::new(); - txn.add_image(image_key, image_desc, image_data, None); - wr.send_transaction(self.webrender_document, txn); - }, - WebGPURequest::CreateTexture { - device_id, - texture_id, - descriptor, - } => { - let global = &self.global; - if let Some(desc) = descriptor { - let result = tuple_to_result( - gfx_select!(texture_id => global.device_create_texture( - device_id, - &desc, - Some(texture_id) - )), - ); - self.send_result(device_id, scope_id, result); - } - }, - WebGPURequest::CreateTextureView { - texture_id, - texture_view_id, - device_id, - descriptor, - } => { - let global = &self.global; - if let Some(desc) = descriptor { - let result = tuple_to_result( - gfx_select!(texture_view_id => global.texture_create_view( - texture_id, - &desc, - Some(texture_view_id) - )), - ); - - self.send_result(device_id, scope_id, result); - } - }, - WebGPURequest::DestroyBuffer(buffer) => { - let global = &self.global; - let _result = gfx_select!(buffer => global.buffer_destroy(buffer)); - }, - WebGPURequest::DestroyDevice(device) => { - let global = &self.global; - gfx_select!(device => global.device_destroy(device)); - }, - WebGPURequest::DestroySwapChain { - external_id, - image_key, - } => { - let data = self - .wgpu_image_map - .lock() - .unwrap() - .remove(&external_id) - .unwrap(); - let global = &self.global; - for b_id in data.available_buffer_ids.iter() { - gfx_select!(b_id => global.buffer_drop(*b_id, false)); - } - for b_id in data.queued_buffer_ids.iter() { - gfx_select!(b_id => global.buffer_drop(*b_id, false)); - } - for b_id in data.unassigned_buffer_ids.iter() { - if let Err(e) = self.script_sender.send(WebGPUMsg::FreeBuffer(*b_id)) { - warn!("Unable to send FreeBuffer({:?}) ({:?})", *b_id, e); - }; - } - let mut txn = Transaction::new(); - txn.delete_image(image_key); - self.webrender_api - .lock() - .unwrap() - .send_transaction(self.webrender_document, txn); - }, - WebGPURequest::DestroyTexture { - device_id, - texture_id, - } => { - let global = &self.global; - let result = gfx_select!(texture_id => global.texture_destroy(texture_id)); - self.send_result(device_id, scope_id, result); - }, - WebGPURequest::Exit(sender) => { - if let Err(e) = sender.send(()) { - warn!("Failed to send response to WebGPURequest::Exit ({})", e) - } - break; - }, - WebGPURequest::DropCommandBuffer(id) => { - self.error_command_encoders - .borrow_mut() - .remove(&id.into_command_encoder_id()); - let global = &self.global; - gfx_select!(id => global.command_buffer_drop(id)); - if let Err(e) = self.script_sender.send(WebGPUMsg::FreeCommandBuffer(id)) { - warn!("Unable to send FreeCommandBuffer({:?}) ({:?})", id, e); - }; - }, - WebGPURequest::DropDevice(device_id) => { - let device = WebGPUDevice(device_id); - let pipeline_id = self.devices.remove(&device).unwrap(); - if let Err(e) = self.script_sender.send(WebGPUMsg::CleanDevice { - device, - pipeline_id, - }) { - warn!("Unable to send CleanDevice({:?}) ({:?})", device_id, e); - } - let global = &self.global; - gfx_select!(device_id => global.device_drop(device_id)); - if let Err(e) = self.script_sender.send(WebGPUMsg::FreeDevice(device_id)) { - warn!("Unable to send FreeDevice({:?}) ({:?})", device_id, e); - }; - }, - WebGPURequest::RenderBundleEncoderFinish { - render_bundle_encoder, - descriptor, - render_bundle_id, - device_id, - } => { - let global = &self.global; - let result = tuple_to_result( - gfx_select!(render_bundle_id => global.render_bundle_encoder_finish( - render_bundle_encoder, - &descriptor, - Some(render_bundle_id) - )), - ); - - self.send_result(device_id, scope_id, result); - }, - WebGPURequest::RequestAdapter { - sender, - options, - ids, - } => { - let adapter_id = match self - .global - .request_adapter(&options, wgpu::instance::AdapterInputs::IdSet(&ids)) - { - Ok(id) => id, - Err(w) => { - if let Err(e) = sender.send(Some(Err(format!("{:?}", w)))) { - warn!( - "Failed to send response to WebGPURequest::RequestAdapter ({})", - e - ) - } - break; - }, - }; - let adapter = WebGPUAdapter(adapter_id); - self.adapters.push(adapter); - let global = &self.global; - // TODO: can we do this lazily - let info = - gfx_select!(adapter_id => global.adapter_get_info(adapter_id)).unwrap(); - let limits = - gfx_select!(adapter_id => global.adapter_limits(adapter_id)).unwrap(); - let features = - gfx_select!(adapter_id => global.adapter_features(adapter_id)).unwrap(); - if let Err(e) = sender.send(Some(Ok(WebGPUResponse::RequestAdapter { - adapter_info: info, - adapter_id: adapter, - features, - limits, - channel: WebGPU(self.sender.clone()), - }))) { - warn!( - "Failed to send response to WebGPURequest::RequestAdapter ({})", - e - ) - } - }, - WebGPURequest::RequestDevice { - sender, - adapter_id, - descriptor, - device_id, - pipeline_id, - } => { - let desc = DeviceDescriptor { - label: descriptor.label.as_ref().map(crate::Cow::from), - required_features: descriptor.required_features, - required_limits: descriptor.required_limits.clone(), - }; - let global = &self.global; - let (device_id, queue_id) = match gfx_select!(device_id => global.adapter_request_device( - adapter_id.0, - &desc, - None, - Some(device_id), - Some(device_id.transmute()), - )) { - (_, _, Some(e)) => { - if let Err(w) = sender.send(Some(Err(format!("{:?}", e)))) { - warn!( - "Failed to send response to WebGPURequest::RequestDevice ({})", - w - ) - } - break; - }, - (device_id, queue_id, None) => (device_id, queue_id), - }; - let device = WebGPUDevice(device_id); - let queue = WebGPUQueue(queue_id); - self.devices.insert(device, pipeline_id); - if let Err(e) = sender.send(Some(Ok(WebGPUResponse::RequestDevice { - device_id: device, - queue_id: queue, - descriptor, - }))) { - warn!( - "Failed to send response to WebGPURequest::RequestDevice ({})", - e - ) - } - }, - WebGPURequest::RunComputePass { - command_encoder_id, - compute_pass, - } => { - let global = &self.global; - let result = if let Some(pass) = compute_pass { - gfx_select!(command_encoder_id => global.command_encoder_run_compute_pass( - command_encoder_id, - &pass - )).map_err(|e| format!("{:?}", e)) - } else { - Err(String::from("Invalid ComputePass")) - }; - self.encoder_record_error(command_encoder_id, &result); - }, - WebGPURequest::RunRenderPass { - command_encoder_id, - render_pass, - } => { - let global = &self.global; - let result = if let Some(pass) = render_pass { - gfx_select!(command_encoder_id => global.command_encoder_run_render_pass( - command_encoder_id, - &pass - )).map_err(|e| format!("{:?}", e)) - } else { - Err(String::from("Invalid RenderPass")) - }; - self.encoder_record_error(command_encoder_id, &result); - }, - WebGPURequest::Submit { - queue_id, - command_buffers, - } => { - let global = &self.global; - let cmd_id = command_buffers.iter().find(|id| { - self.error_command_encoders - .borrow() - .contains_key(&id.into_command_encoder_id()) - }); - let result = if cmd_id.is_some() { - Err(String::from("Invalid command buffer submitted")) - } else { - gfx_select!(queue_id => global.queue_submit(queue_id, &command_buffers)) - .map_err(|e| format!("{:?}", e)) - }; - self.send_result(queue_id.transmute(), scope_id, result); - }, - WebGPURequest::SwapChainPresent { - external_id, - texture_id, - encoder_id, - } => { - let global = &self.global; - let device_id; - let queue_id; - let size; - let buffer_id; - let buffer_stride; - { - if let Some(present_data) = - self.wgpu_image_map.lock().unwrap().get_mut(&external_id) - { - size = present_data.size; - device_id = present_data.device_id; - queue_id = present_data.queue_id; - buffer_stride = present_data.buffer_stride; - buffer_id = if let Some(b_id) = - present_data.available_buffer_ids.pop() - { - b_id - } else if let Some(b_id) = present_data.unassigned_buffer_ids.pop() - { - let buffer_size = - (buffer_stride * size.height as u32) as wgt::BufferAddress; - let buffer_desc = wgt::BufferDescriptor { - label: None, - size: buffer_size, - usage: wgt::BufferUsages::MAP_READ | - wgt::BufferUsages::COPY_DST, - mapped_at_creation: false, - }; - let _ = gfx_select!(b_id => global.device_create_buffer( - device_id, - &buffer_desc, - Some(b_id) - )); - b_id - } else { - warn!( - "No staging buffer available for ExternalImageId({:?})", - external_id - ); - continue; - }; - present_data.queued_buffer_ids.push(buffer_id); - } else { - warn!("Data not found for ExternalImageId({:?})", external_id); - continue; - } - } - - let buffer_size = - (size.height as u32 * buffer_stride) as wgt::BufferAddress; - let comm_desc = wgt::CommandEncoderDescriptor { label: None }; - let _ = gfx_select!(encoder_id => global.device_create_command_encoder( - device_id, - &comm_desc, - Some(encoder_id) - )); - - let buffer_cv = ImageCopyBuffer { - buffer: buffer_id, - layout: wgt::ImageDataLayout { - offset: 0, - bytes_per_row: Some(buffer_stride), - rows_per_image: None, - }, - }; - let texture_cv = ImageCopyTexture { - texture: texture_id, - mip_level: 0, - origin: wgt::Origin3d::ZERO, - aspect: wgt::TextureAspect::All, - }; - let copy_size = wgt::Extent3d { - width: size.width as u32, - height: size.height as u32, - depth_or_array_layers: 1, - }; - let _ = gfx_select!(encoder_id => global.command_encoder_copy_texture_to_buffer( - encoder_id, - &texture_cv, - &buffer_cv, - ©_size - )); - let _ = gfx_select!(encoder_id => global.command_encoder_finish( - encoder_id, - &wgt::CommandBufferDescriptor::default() - )); - let _ = gfx_select!(queue_id => global.queue_submit( - queue_id, - &[encoder_id.into_command_buffer_id()] - )); - - let glob = Arc::clone(&self.global); - let wgpu_image_map = Arc::clone(&self.wgpu_image_map); - let webrender_api = Arc::clone(&self.webrender_api); - let webrender_document = self.webrender_document; - let callback = BufferMapCallback::from_rust(Box::from(move |result| { - match result { - Ok(()) => { - let global = &glob; - let (slice_pointer, range_size) = gfx_select!(buffer_id => - global.buffer_get_mapped_range(buffer_id, 0, Some(buffer_size as u64))) - .unwrap(); - let data = unsafe { - slice::from_raw_parts(slice_pointer, range_size as usize) - } - .to_vec(); - if let Some(present_data) = - wgpu_image_map.lock().unwrap().get_mut(&external_id) - { - present_data.data = data; - let mut txn = Transaction::new(); - txn.update_image( - present_data.image_key, - present_data.image_desc, - present_data.image_data.clone(), - &DirtyRect::All, - ); - webrender_api - .lock() - .unwrap() - .send_transaction(webrender_document, txn); - present_data - .queued_buffer_ids - .retain(|b_id| *b_id != buffer_id); - present_data.available_buffer_ids.push(buffer_id); - } else { - warn!( - "Data not found for ExternalImageId({:?})", - external_id - ); - } - let _ = - gfx_select!(buffer_id => global.buffer_unmap(buffer_id)); - }, - _ => error!("Could not map buffer({:?})", buffer_id), - } - })); - let map_op = BufferMapOperation { - host: HostMap::Read, - callback: Some(callback), - }; - let _ = gfx_select!(buffer_id - => global.buffer_map_async(buffer_id, 0, Some(buffer_size), map_op)); - }, - WebGPURequest::UnmapBuffer { - buffer_id, - device_id, - array_buffer, - is_map_read, - offset, - size, - } => { - let global = &self.global; - if !is_map_read { - let (slice_pointer, range_size) = - gfx_select!(buffer_id => global.buffer_get_mapped_range( - buffer_id, - offset, - Some(size) - )) - .unwrap(); - unsafe { - slice::from_raw_parts_mut(slice_pointer, range_size as usize) - } - .copy_from_slice(&array_buffer); - } - let result = gfx_select!(buffer_id => global.buffer_unmap(buffer_id)); - self.send_result(device_id, scope_id, result); - }, - WebGPURequest::WriteBuffer { - queue_id, - buffer_id, - buffer_offset, - data, - } => { - let global = &self.global; - //TODO: Report result to content process - let result = gfx_select!(queue_id => global.queue_write_buffer( - queue_id, - buffer_id, - buffer_offset as wgt::BufferAddress, - &data - )); - self.send_result(queue_id.transmute(), scope_id, result); - }, - WebGPURequest::WriteTexture { - queue_id, - texture_cv, - data_layout, - size, - data, - } => { - let global = &self.global; - //TODO: Report result to content process - let result = gfx_select!(queue_id => global.queue_write_texture( - queue_id, - &texture_cv, - &data, - &data_layout, - &size - )); - self.send_result(queue_id.transmute(), scope_id, result); - }, - WebGPURequest::QueueOnSubmittedWorkDone { sender, queue_id } => { - let global = &self.global; - - let callback = SubmittedWorkDoneClosure::from_rust(Box::from(move || { - if let Err(e) = sender.send(Some(Ok(WebGPUResponse::SubmittedWorkDone))) - { - warn!("Could not send SubmittedWorkDone Response ({})", e); - } - })); - let result = gfx_select!(queue_id => global.queue_on_submitted_work_done(queue_id, callback)); - self.send_result(queue_id.transmute(), scope_id, result); - }, - WebGPURequest::DropTexture(id) => { - let global = &self.global; - gfx_select!(id => global.texture_drop(id, true)); - if let Err(e) = self.script_sender.send(WebGPUMsg::FreeTexture(id)) { - warn!("Unable to send FreeTexture({:?}) ({:?})", id, e); - }; - }, - WebGPURequest::DropAdapter(id) => { - let global = &self.global; - gfx_select!(id => global.adapter_drop(id)); - if let Err(e) = self.script_sender.send(WebGPUMsg::FreeAdapter(id)) { - warn!("Unable to send FreeAdapter({:?}) ({:?})", id, e); - }; - }, - WebGPURequest::DropBuffer(id) => { - let global = &self.global; - gfx_select!(id => global.buffer_drop(id, true)); - if let Err(e) = self.script_sender.send(WebGPUMsg::FreeBuffer(id)) { - warn!("Unable to send FreeBuffer({:?}) ({:?})", id, e); - }; - }, - WebGPURequest::DropPipelineLayout(id) => { - let global = &self.global; - gfx_select!(id => global.pipeline_layout_drop(id)); - if let Err(e) = self.script_sender.send(WebGPUMsg::FreePipelineLayout(id)) { - warn!("Unable to send FreePipelineLayout({:?}) ({:?})", id, e); - }; - }, - WebGPURequest::DropComputePipeline(id) => { - let global = &self.global; - gfx_select!(id => global.compute_pipeline_drop(id)); - if let Err(e) = self.script_sender.send(WebGPUMsg::FreeComputePipeline(id)) - { - warn!("Unable to send FreeComputePipeline({:?}) ({:?})", id, e); - }; - }, - WebGPURequest::DropRenderPipeline(id) => { - let global = &self.global; - gfx_select!(id => global.render_pipeline_drop(id)); - if let Err(e) = self.script_sender.send(WebGPUMsg::FreeRenderPipeline(id)) { - warn!("Unable to send FreeRenderPipeline({:?}) ({:?})", id, e); - }; - }, - WebGPURequest::DropBindGroup(id) => { - let global = &self.global; - gfx_select!(id => global.bind_group_drop(id)); - if let Err(e) = self.script_sender.send(WebGPUMsg::FreeBindGroup(id)) { - warn!("Unable to send FreeBindGroup({:?}) ({:?})", id, e); - }; - }, - WebGPURequest::DropBindGroupLayout(id) => { - let global = &self.global; - gfx_select!(id => global.bind_group_layout_drop(id)); - if let Err(e) = self.script_sender.send(WebGPUMsg::FreeBindGroupLayout(id)) - { - warn!("Unable to send FreeBindGroupLayout({:?}) ({:?})", id, e); - }; - }, - WebGPURequest::DropTextureView(id) => { - let global = &self.global; - let _result = gfx_select!(id => global.texture_view_drop(id, true)); - if let Err(e) = self.script_sender.send(WebGPUMsg::FreeTextureView(id)) { - warn!("Unable to send FreeTextureView({:?}) ({:?})", id, e); - }; - }, - WebGPURequest::DropSampler(id) => { - let global = &self.global; - gfx_select!(id => global.sampler_drop(id)); - if let Err(e) = self.script_sender.send(WebGPUMsg::FreeSampler(id)) { - warn!("Unable to send FreeSampler({:?}) ({:?})", id, e); - }; - }, - WebGPURequest::DropShaderModule(id) => { - let global = &self.global; - gfx_select!(id => global.shader_module_drop(id)); - if let Err(e) = self.script_sender.send(WebGPUMsg::FreeShaderModule(id)) { - warn!("Unable to send FreeShaderModule({:?}) ({:?})", id, e); - }; - }, - WebGPURequest::DropRenderBundle(id) => { - let global = &self.global; - gfx_select!(id => global.render_bundle_drop(id)); - if let Err(e) = self.script_sender.send(WebGPUMsg::FreeRenderBundle(id)) { - warn!("Unable to send FreeRenderBundle({:?}) ({:?})", id, e); - }; - }, - WebGPURequest::DropQuerySet(id) => { - let global = &self.global; - gfx_select!(id => global.query_set_drop(id)); - if let Err(e) = self.script_sender.send(WebGPUMsg::FreeQuerySet(id)) { - warn!("Unable to send FreeQuerySet({:?}) ({:?})", id, e); - }; - }, - } - } - } - if let Err(e) = self.script_sender.send(WebGPUMsg::Exit) { - warn!("Failed to send WebGPUMsg::Exit to script ({})", e); - } - } - - fn send_result<U, T: std::fmt::Debug>( - &self, - device_id: id::DeviceId, - scope_id: Option<ErrorScopeId>, - result: Result<U, T>, - ) { - let &pipeline_id = self.devices.get(&WebGPUDevice(device_id)).unwrap(); - if let Err(w) = self.script_sender.send(WebGPUMsg::WebGPUOpResult { - device: WebGPUDevice(device_id), - scope_id, - pipeline_id, - result: if let Err(e) = result { - let err = format!("{:?}", e); - if err.contains("OutOfMemory") { - WebGPUOpResult::OutOfMemoryError - } else { - WebGPUOpResult::ValidationError(err) - } - } else { - WebGPUOpResult::Success - }, - }) { - warn!("Failed to send WebGPUOpResult ({})", w); - } - } - - fn encoder_record_error<U, T: std::fmt::Debug>( - &self, - encoder_id: id::CommandEncoderId, - result: &Result<U, T>, - ) { - if let Err(ref e) = result { - self.error_command_encoders - .borrow_mut() - .entry(encoder_id) - .or_insert_with(|| format!("{:?}", e)); - } - } -} - -fn tuple_to_result<T, E>(res: (T, Option<E>)) -> Result<T, E> { - if let Some(err) = res.1 { - Err(err) - } else { - Ok(res.0) - } -} - -macro_rules! webgpu_resource { - ($name:ident, $id:ty) => { - #[derive(Clone, Copy, Debug, Deserialize, Hash, PartialEq, Serialize)] - pub struct $name(pub $id); - - impl MallocSizeOf for $name { - fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { - 0 - } - } - - impl Eq for $name {} - }; -} - -webgpu_resource!(WebGPUAdapter, id::AdapterId); -webgpu_resource!(WebGPUBindGroup, id::BindGroupId); -webgpu_resource!(WebGPUBindGroupLayout, id::BindGroupLayoutId); -webgpu_resource!(WebGPUBuffer, id::BufferId); -webgpu_resource!(WebGPUCommandBuffer, id::CommandBufferId); -webgpu_resource!(WebGPUCommandEncoder, id::CommandEncoderId); -webgpu_resource!(WebGPUComputePipeline, id::ComputePipelineId); -webgpu_resource!(WebGPUDevice, id::DeviceId); -webgpu_resource!(WebGPUPipelineLayout, id::PipelineLayoutId); -webgpu_resource!(WebGPUQueue, id::QueueId); -webgpu_resource!(WebGPURenderBundle, id::RenderBundleId); -webgpu_resource!(WebGPURenderPipeline, id::RenderPipelineId); -webgpu_resource!(WebGPUSampler, id::SamplerId); -webgpu_resource!(WebGPUShaderModule, id::ShaderModuleId); -webgpu_resource!(WebGPUSurface, id::SurfaceId); -webgpu_resource!(WebGPUTexture, id::TextureId); -webgpu_resource!(WebGPUTextureView, id::TextureViewId); - #[derive(Default)] pub struct WGPUExternalImages { pub images: Arc<Mutex<HashMap<u64, PresentationData>>>, diff --git a/components/webgpu/script_messages.rs b/components/webgpu/script_messages.rs new file mode 100644 index 00000000000..08e2269d39c --- /dev/null +++ b/components/webgpu/script_messages.rs @@ -0,0 +1,55 @@ +/* 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/. */ + +//! IPC massages that are send to script thread (global scope). + +use msg::constellation_msg::PipelineId; +use serde::{Deserialize, Serialize}; + +use crate::identity::WebGPUDevice; +use crate::wgc::id::{ + AdapterId, BindGroupId, BindGroupLayoutId, BufferId, CommandBufferId, ComputePipelineId, + DeviceId, PipelineLayoutId, QuerySetId, RenderBundleId, RenderPipelineId, SamplerId, + ShaderModuleId, StagingBufferId, SurfaceId, TextureId, TextureViewId, +}; +use crate::ErrorScopeId; + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub enum WebGPUOpResult { + ValidationError(String), + OutOfMemoryError, + Success, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub enum WebGPUMsg { + FreeAdapter(AdapterId), + FreeDevice(DeviceId), + FreeBuffer(BufferId), + FreePipelineLayout(PipelineLayoutId), + FreeComputePipeline(ComputePipelineId), + FreeRenderPipeline(RenderPipelineId), + FreeBindGroup(BindGroupId), + FreeBindGroupLayout(BindGroupLayoutId), + FreeCommandBuffer(CommandBufferId), + FreeTexture(TextureId), + FreeTextureView(TextureViewId), + FreeSampler(SamplerId), + FreeSurface(SurfaceId), + FreeShaderModule(ShaderModuleId), + FreeRenderBundle(RenderBundleId), + FreeStagingBuffer(StagingBufferId), + FreeQuerySet(QuerySetId), + WebGPUOpResult { + device: WebGPUDevice, + scope_id: Option<ErrorScopeId>, + pipeline_id: PipelineId, + result: WebGPUOpResult, + }, + CleanDevice { + device: WebGPUDevice, + pipeline_id: PipelineId, + }, + Exit, +} diff --git a/components/webgpu/wgpu_thread.rs b/components/webgpu/wgpu_thread.rs new file mode 100644 index 00000000000..16bda74634e --- /dev/null +++ b/components/webgpu/wgpu_thread.rs @@ -0,0 +1,1088 @@ +/* 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/. */ + +//! Data and main loop of WebGPU thread. + +use std::cell::RefCell; +use std::collections::HashMap; +use std::slice; +use std::sync::{Arc, Mutex}; +use std::time::{Duration, Instant}; + +use arrayvec::ArrayVec; +use euclid::default::Size2D; +use ipc_channel::ipc::{IpcReceiver, IpcSender, IpcSharedMemory}; +use log::{error, warn}; +use msg::constellation_msg::PipelineId; +use webrender::{RenderApi, RenderApiSender, Transaction}; +use webrender_api::{DirtyRect, DocumentId}; +use webrender_traits::{WebrenderExternalImageRegistry, WebrenderImageHandlerType}; +use wgc::command::{ImageCopyBuffer, ImageCopyTexture}; +use wgc::device::queue::SubmittedWorkDoneClosure; +use wgc::device::{DeviceDescriptor, HostMap, ImplicitPipelineIds}; +use wgc::pipeline::ShaderModuleDescriptor; +use wgc::resource::{BufferMapCallback, BufferMapOperation}; +use wgc::{gfx_select, id}; +use wgt::InstanceDescriptor; +pub use {wgpu_core as wgc, wgpu_types as wgt}; + +use crate::{ + ErrorScopeId, PresentationData, Transmute, WebGPU, WebGPUAdapter, WebGPUDevice, WebGPUMsg, + WebGPUOpResult, WebGPUQueue, WebGPURequest, WebGPUResponse, +}; + +const DEVICE_POLL_INTERVAL: Duration = Duration::from_millis(50); +pub const PRESENTATION_BUFFER_COUNT: usize = 10; + +#[allow(clippy::upper_case_acronyms)] // Name of the library +pub(crate) struct WGPU { + receiver: IpcReceiver<(Option<ErrorScopeId>, WebGPURequest)>, + sender: IpcSender<(Option<ErrorScopeId>, WebGPURequest)>, + script_sender: IpcSender<WebGPUMsg>, + global: Arc<wgc::global::Global>, + adapters: Vec<WebGPUAdapter>, + devices: HashMap<WebGPUDevice, PipelineId>, + // Track invalid adapters https://gpuweb.github.io/gpuweb/#invalid + _invalid_adapters: Vec<WebGPUAdapter>, + //TODO: Remove this (https://github.com/gfx-rs/wgpu/issues/867) + error_command_encoders: RefCell<HashMap<id::CommandEncoderId, String>>, + webrender_api: Arc<Mutex<RenderApi>>, + webrender_document: DocumentId, + external_images: Arc<Mutex<WebrenderExternalImageRegistry>>, + wgpu_image_map: Arc<Mutex<HashMap<u64, PresentationData>>>, + last_poll: Instant, +} + +impl WGPU { + pub(crate) fn new( + receiver: IpcReceiver<(Option<ErrorScopeId>, WebGPURequest)>, + sender: IpcSender<(Option<ErrorScopeId>, WebGPURequest)>, + script_sender: IpcSender<WebGPUMsg>, + webrender_api_sender: RenderApiSender, + webrender_document: DocumentId, + external_images: Arc<Mutex<WebrenderExternalImageRegistry>>, + wgpu_image_map: Arc<Mutex<HashMap<u64, PresentationData>>>, + ) -> Self { + WGPU { + receiver, + sender, + script_sender, + global: Arc::new(wgc::global::Global::new( + "wgpu-core", + InstanceDescriptor { + backends: wgt::Backends::PRIMARY, + ..Default::default() + }, + )), + adapters: Vec::new(), + devices: HashMap::new(), + _invalid_adapters: Vec::new(), + error_command_encoders: RefCell::new(HashMap::new()), + webrender_api: Arc::new(Mutex::new(webrender_api_sender.create_api())), + webrender_document, + external_images, + wgpu_image_map, + last_poll: Instant::now(), + } + } + + pub(crate) fn run(&mut self) { + loop { + let diff = DEVICE_POLL_INTERVAL.checked_sub(self.last_poll.elapsed()); + if diff.is_none() { + let _ = self.global.poll_all_devices(false); + self.last_poll = Instant::now(); + } + if let Ok((scope_id, msg)) = self + .receiver + .try_recv_timeout(diff.unwrap_or(DEVICE_POLL_INTERVAL)) + { + match msg { + WebGPURequest::BufferMapAsync { + sender, + buffer_id, + device_id, + host_map, + offset, + size, + } => { + let glob = Arc::clone(&self.global); + let resp_sender = sender.clone(); + let callback = BufferMapCallback::from_rust(Box::from(move |result| { + match result { + Ok(()) => { + let global = &glob; + let (slice_pointer, range_size) = gfx_select!(buffer_id => + global.buffer_get_mapped_range(buffer_id, 0, None)) + .unwrap(); + // SAFETY: guarantee to be safe from wgpu + let data = unsafe { + slice::from_raw_parts(slice_pointer, range_size as usize) + }; + + if let Err(e) = + resp_sender.send(Some(Ok(WebGPUResponse::BufferMapAsync( + IpcSharedMemory::from_bytes(data), + )))) + { + warn!("Could not send BufferMapAsync Response ({})", e); + } + }, + Err(_) => { + warn!("Could not map buffer({:?})", buffer_id); + if let Err(e) = resp_sender + .send(Some(Err(String::from("Failed to map Buffer")))) + { + warn!("Could not send BufferMapAsync Response ({})", e); + } + }, + }; + })); + + let operation = BufferMapOperation { + host: host_map, + callback: Some(callback), + }; + let global = &self.global; + let result = gfx_select!(buffer_id => global.buffer_map_async( + buffer_id, + offset, + size, + operation + )); + if let Err(ref e) = result { + if let Err(w) = sender.send(Some(Err(format!("{:?}", e)))) { + warn!("Failed to send BufferMapAsync Response ({:?})", w); + } + } + self.send_result(device_id, scope_id, result); + }, + WebGPURequest::CommandEncoderFinish { + command_encoder_id, + device_id, + is_error, + } => { + let global = &self.global; + let result = if is_error { + Err(String::from("Invalid GPUCommandEncoder")) + } else if let Some(err) = self + .error_command_encoders + .borrow() + .get(&command_encoder_id) + { + Err(err.clone()) + } else { + tuple_to_result( + gfx_select!(command_encoder_id => global.command_encoder_finish( + command_encoder_id, + &wgt::CommandBufferDescriptor::default() + )), + ) + .map_err(|e| format!("{:?}", e)) + }; + self.encoder_record_error(command_encoder_id, &result); + self.send_result(device_id, scope_id, result); + }, + WebGPURequest::CopyBufferToBuffer { + command_encoder_id, + source_id, + source_offset, + destination_id, + destination_offset, + size, + } => { + let global = &self.global; + let result = gfx_select!(command_encoder_id => global.command_encoder_copy_buffer_to_buffer( + command_encoder_id, + source_id, + source_offset, + destination_id, + destination_offset, + size + )); + self.encoder_record_error(command_encoder_id, &result); + }, + WebGPURequest::CopyBufferToTexture { + command_encoder_id, + source, + destination, + copy_size, + } => { + let global = &self.global; + let result = gfx_select!(command_encoder_id => global.command_encoder_copy_buffer_to_texture( + command_encoder_id, + &source, + &destination, + ©_size + )); + self.encoder_record_error(command_encoder_id, &result); + }, + WebGPURequest::CopyTextureToBuffer { + command_encoder_id, + source, + destination, + copy_size, + } => { + let global = &self.global; + let result = gfx_select!(command_encoder_id => global.command_encoder_copy_texture_to_buffer( + command_encoder_id, + &source, + &destination, + ©_size + )); + self.encoder_record_error(command_encoder_id, &result); + }, + WebGPURequest::CopyTextureToTexture { + command_encoder_id, + source, + destination, + copy_size, + } => { + let global = &self.global; + let result = gfx_select!(command_encoder_id => global.command_encoder_copy_texture_to_texture( + command_encoder_id, + &source, + &destination, + ©_size + )); + self.encoder_record_error(command_encoder_id, &result); + }, + WebGPURequest::CreateBindGroup { + device_id, + bind_group_id, + descriptor, + } => { + let global = &self.global; + let result = tuple_to_result(gfx_select!(bind_group_id => + global.device_create_bind_group(device_id, &descriptor, Some(bind_group_id)))); + self.send_result(device_id, scope_id, result); + }, + WebGPURequest::CreateBindGroupLayout { + device_id, + bind_group_layout_id, + descriptor, + } => { + let global = &self.global; + if let Some(desc) = descriptor { + let result = tuple_to_result(gfx_select!(bind_group_layout_id => + global.device_create_bind_group_layout(device_id, &desc, Some(bind_group_layout_id)))); + + self.send_result(device_id, scope_id, result); + } + }, + WebGPURequest::CreateBuffer { + device_id, + buffer_id, + descriptor, + } => { + let global = &self.global; + if let Some(desc) = descriptor { + let result = tuple_to_result(gfx_select!(buffer_id => + global.device_create_buffer(device_id, &desc, Some(buffer_id)))); + + self.send_result(device_id, scope_id, result); + } + }, + WebGPURequest::CreateCommandEncoder { + device_id, + command_encoder_id, + label, + } => { + let global = &self.global; + let desc = wgt::CommandEncoderDescriptor { label }; + let result = tuple_to_result(gfx_select!(command_encoder_id => + global.device_create_command_encoder(device_id, &desc, Some(command_encoder_id)))); + + self.send_result(device_id, scope_id, result); + }, + WebGPURequest::CreateComputePipeline { + device_id, + compute_pipeline_id, + descriptor, + implicit_ids, + } => { + let global = &self.global; + let bgls = implicit_ids + .as_ref() + .map_or(Vec::with_capacity(0), |(_, bgls)| { + bgls.iter().map(|x| Some(x.to_owned())).collect() + }); + let implicit = + implicit_ids + .as_ref() + .map(|(layout, _)| ImplicitPipelineIds { + root_id: Some(*layout), + group_ids: bgls.as_slice(), + }); + let result = tuple_to_result( + gfx_select!(compute_pipeline_id => global.device_create_compute_pipeline( + device_id, + &descriptor, + Some(compute_pipeline_id), + implicit + )), + ); + self.send_result(device_id, scope_id, result); + }, + WebGPURequest::CreateContext(sender) => { + let id = self + .external_images + .lock() + .expect("Lock poisoned?") + .next_id(WebrenderImageHandlerType::WebGPU); + if let Err(e) = sender.send(id) { + warn!("Failed to send ExternalImageId to new context ({})", e); + }; + }, + WebGPURequest::CreatePipelineLayout { + device_id, + pipeline_layout_id, + descriptor, + } => { + let global = &self.global; + let result = tuple_to_result(gfx_select!(pipeline_layout_id => + global.device_create_pipeline_layout(device_id, &descriptor, Some(pipeline_layout_id)))); + self.send_result(device_id, scope_id, result); + }, + WebGPURequest::CreateRenderPipeline { + device_id, + render_pipeline_id, + descriptor, + implicit_ids, + } => { + let global = &self.global; + let bgls = implicit_ids + .as_ref() + .map_or(Vec::with_capacity(0), |(_, bgls)| { + bgls.iter().map(|x| Some(x.to_owned())).collect() + }); + let implicit = + implicit_ids + .as_ref() + .map(|(layout, _)| ImplicitPipelineIds { + root_id: Some(*layout), + group_ids: bgls.as_slice(), + }); + if let Some(desc) = descriptor { + let result = tuple_to_result(gfx_select!(render_pipeline_id => + global.device_create_render_pipeline( + device_id, + &desc, + Some(render_pipeline_id), + implicit) + )); + self.send_result(device_id, scope_id, result); + } + }, + WebGPURequest::CreateSampler { + device_id, + sampler_id, + descriptor, + } => { + let global = &self.global; + let result = tuple_to_result( + gfx_select!(sampler_id => global.device_create_sampler( + device_id, + &descriptor, + Some(sampler_id) + )), + ); + self.send_result(device_id, scope_id, result); + }, + WebGPURequest::CreateShaderModule { + device_id, + program_id, + program, + label, + } => { + let global = &self.global; + let source = wgpu_core::pipeline::ShaderModuleSource::Wgsl( + crate::Cow::Owned(program), + ); + let desc = ShaderModuleDescriptor { + label: label.map(|s| s.into()), + shader_bound_checks: wgt::ShaderBoundChecks::default(), + }; + let result = tuple_to_result(gfx_select!(program_id => + global.device_create_shader_module(device_id, &desc, source, Some(program_id)))); + self.send_result(device_id, scope_id, result); + }, + WebGPURequest::CreateSwapChain { + device_id, + buffer_ids, + external_id, + sender, + image_desc, + image_data, + } => { + let height = image_desc.size.height; + let width = image_desc.size.width; + let buffer_stride = + ((width * 4) as u32 | (wgt::COPY_BYTES_PER_ROW_ALIGNMENT - 1)) + 1; + let mut wr = self.webrender_api.lock().unwrap(); + let image_key = wr.generate_image_key(); + if let Err(e) = sender.send(image_key) { + warn!("Failed to send ImageKey ({})", e); + } + let _ = self.wgpu_image_map.lock().unwrap().insert( + external_id, + PresentationData { + device_id, + queue_id: device_id.transmute(), + data: vec![255; (buffer_stride * height as u32) as usize], + size: Size2D::new(width, height), + unassigned_buffer_ids: buffer_ids, + available_buffer_ids: ArrayVec::< + id::BufferId, + PRESENTATION_BUFFER_COUNT, + >::new(), + queued_buffer_ids: ArrayVec::< + id::BufferId, + PRESENTATION_BUFFER_COUNT, + >::new(), + buffer_stride, + image_key, + image_desc, + image_data: image_data.clone(), + }, + ); + + let mut txn = Transaction::new(); + txn.add_image(image_key, image_desc, image_data, None); + wr.send_transaction(self.webrender_document, txn); + }, + WebGPURequest::CreateTexture { + device_id, + texture_id, + descriptor, + } => { + let global = &self.global; + if let Some(desc) = descriptor { + let result = tuple_to_result( + gfx_select!(texture_id => global.device_create_texture( + device_id, + &desc, + Some(texture_id) + )), + ); + self.send_result(device_id, scope_id, result); + } + }, + WebGPURequest::CreateTextureView { + texture_id, + texture_view_id, + device_id, + descriptor, + } => { + let global = &self.global; + if let Some(desc) = descriptor { + let result = tuple_to_result( + gfx_select!(texture_view_id => global.texture_create_view( + texture_id, + &desc, + Some(texture_view_id) + )), + ); + + self.send_result(device_id, scope_id, result); + } + }, + WebGPURequest::DestroyBuffer(buffer) => { + let global = &self.global; + let _result = gfx_select!(buffer => global.buffer_destroy(buffer)); + }, + WebGPURequest::DestroyDevice(device) => { + let global = &self.global; + gfx_select!(device => global.device_destroy(device)); + }, + WebGPURequest::DestroySwapChain { + external_id, + image_key, + } => { + let data = self + .wgpu_image_map + .lock() + .unwrap() + .remove(&external_id) + .unwrap(); + let global = &self.global; + for b_id in data.available_buffer_ids.iter() { + gfx_select!(b_id => global.buffer_drop(*b_id, false)); + } + for b_id in data.queued_buffer_ids.iter() { + gfx_select!(b_id => global.buffer_drop(*b_id, false)); + } + for b_id in data.unassigned_buffer_ids.iter() { + if let Err(e) = self.script_sender.send(WebGPUMsg::FreeBuffer(*b_id)) { + warn!("Unable to send FreeBuffer({:?}) ({:?})", *b_id, e); + }; + } + let mut txn = Transaction::new(); + txn.delete_image(image_key); + self.webrender_api + .lock() + .unwrap() + .send_transaction(self.webrender_document, txn); + }, + WebGPURequest::DestroyTexture { + device_id, + texture_id, + } => { + let global = &self.global; + let result = gfx_select!(texture_id => global.texture_destroy(texture_id)); + self.send_result(device_id, scope_id, result); + }, + WebGPURequest::Exit(sender) => { + if let Err(e) = sender.send(()) { + warn!("Failed to send response to WebGPURequest::Exit ({})", e) + } + break; + }, + WebGPURequest::DropCommandBuffer(id) => { + self.error_command_encoders + .borrow_mut() + .remove(&id.into_command_encoder_id()); + let global = &self.global; + gfx_select!(id => global.command_buffer_drop(id)); + if let Err(e) = self.script_sender.send(WebGPUMsg::FreeCommandBuffer(id)) { + warn!("Unable to send FreeCommandBuffer({:?}) ({:?})", id, e); + }; + }, + WebGPURequest::DropDevice(device_id) => { + let device = WebGPUDevice(device_id); + let pipeline_id = self.devices.remove(&device).unwrap(); + if let Err(e) = self.script_sender.send(WebGPUMsg::CleanDevice { + device, + pipeline_id, + }) { + warn!("Unable to send CleanDevice({:?}) ({:?})", device_id, e); + } + let global = &self.global; + gfx_select!(device_id => global.device_drop(device_id)); + if let Err(e) = self.script_sender.send(WebGPUMsg::FreeDevice(device_id)) { + warn!("Unable to send FreeDevice({:?}) ({:?})", device_id, e); + }; + }, + WebGPURequest::RenderBundleEncoderFinish { + render_bundle_encoder, + descriptor, + render_bundle_id, + device_id, + } => { + let global = &self.global; + let result = tuple_to_result( + gfx_select!(render_bundle_id => global.render_bundle_encoder_finish( + render_bundle_encoder, + &descriptor, + Some(render_bundle_id) + )), + ); + + self.send_result(device_id, scope_id, result); + }, + WebGPURequest::RequestAdapter { + sender, + options, + ids, + } => { + let adapter_id = match self + .global + .request_adapter(&options, wgc::instance::AdapterInputs::IdSet(&ids)) + { + Ok(id) => id, + Err(w) => { + if let Err(e) = sender.send(Some(Err(format!("{:?}", w)))) { + warn!( + "Failed to send response to WebGPURequest::RequestAdapter ({})", + e + ) + } + break; + }, + }; + let adapter = WebGPUAdapter(adapter_id); + self.adapters.push(adapter); + let global = &self.global; + // TODO: can we do this lazily + let info = + gfx_select!(adapter_id => global.adapter_get_info(adapter_id)).unwrap(); + let limits = + gfx_select!(adapter_id => global.adapter_limits(adapter_id)).unwrap(); + let features = + gfx_select!(adapter_id => global.adapter_features(adapter_id)).unwrap(); + if let Err(e) = sender.send(Some(Ok(WebGPUResponse::RequestAdapter { + adapter_info: info, + adapter_id: adapter, + features, + limits, + channel: WebGPU(self.sender.clone()), + }))) { + warn!( + "Failed to send response to WebGPURequest::RequestAdapter ({})", + e + ) + } + }, + WebGPURequest::RequestDevice { + sender, + adapter_id, + descriptor, + device_id, + pipeline_id, + } => { + let desc = DeviceDescriptor { + label: descriptor.label.as_ref().map(crate::Cow::from), + required_features: descriptor.required_features, + required_limits: descriptor.required_limits.clone(), + }; + let global = &self.global; + let (device_id, queue_id) = match gfx_select!(device_id => global.adapter_request_device( + adapter_id.0, + &desc, + None, + Some(device_id), + Some(device_id.transmute()), + )) { + (_, _, Some(e)) => { + if let Err(w) = sender.send(Some(Err(format!("{:?}", e)))) { + warn!( + "Failed to send response to WebGPURequest::RequestDevice ({})", + w + ) + } + break; + }, + (device_id, queue_id, None) => (device_id, queue_id), + }; + let device = WebGPUDevice(device_id); + let queue = WebGPUQueue(queue_id); + self.devices.insert(device, pipeline_id); + if let Err(e) = sender.send(Some(Ok(WebGPUResponse::RequestDevice { + device_id: device, + queue_id: queue, + descriptor, + }))) { + warn!( + "Failed to send response to WebGPURequest::RequestDevice ({})", + e + ) + } + }, + WebGPURequest::RunComputePass { + command_encoder_id, + compute_pass, + } => { + let global = &self.global; + let result = if let Some(pass) = compute_pass { + gfx_select!(command_encoder_id => global.command_encoder_run_compute_pass( + command_encoder_id, + &pass + )).map_err(|e| format!("{:?}", e)) + } else { + Err(String::from("Invalid ComputePass")) + }; + self.encoder_record_error(command_encoder_id, &result); + }, + WebGPURequest::RunRenderPass { + command_encoder_id, + render_pass, + } => { + let global = &self.global; + let result = if let Some(pass) = render_pass { + gfx_select!(command_encoder_id => global.command_encoder_run_render_pass( + command_encoder_id, + &pass + )).map_err(|e| format!("{:?}", e)) + } else { + Err(String::from("Invalid RenderPass")) + }; + self.encoder_record_error(command_encoder_id, &result); + }, + WebGPURequest::Submit { + queue_id, + command_buffers, + } => { + let global = &self.global; + let cmd_id = command_buffers.iter().find(|id| { + self.error_command_encoders + .borrow() + .contains_key(&id.into_command_encoder_id()) + }); + let result = if cmd_id.is_some() { + Err(String::from("Invalid command buffer submitted")) + } else { + gfx_select!(queue_id => global.queue_submit(queue_id, &command_buffers)) + .map_err(|e| format!("{:?}", e)) + }; + self.send_result(queue_id.transmute(), scope_id, result); + }, + WebGPURequest::SwapChainPresent { + external_id, + texture_id, + encoder_id, + } => { + let global = &self.global; + let device_id; + let queue_id; + let size; + let buffer_id; + let buffer_stride; + { + if let Some(present_data) = + self.wgpu_image_map.lock().unwrap().get_mut(&external_id) + { + size = present_data.size; + device_id = present_data.device_id; + queue_id = present_data.queue_id; + buffer_stride = present_data.buffer_stride; + buffer_id = if let Some(b_id) = + present_data.available_buffer_ids.pop() + { + b_id + } else if let Some(b_id) = present_data.unassigned_buffer_ids.pop() + { + let buffer_size = + (buffer_stride * size.height as u32) as wgt::BufferAddress; + let buffer_desc = wgt::BufferDescriptor { + label: None, + size: buffer_size, + usage: wgt::BufferUsages::MAP_READ | + wgt::BufferUsages::COPY_DST, + mapped_at_creation: false, + }; + let _ = gfx_select!(b_id => global.device_create_buffer( + device_id, + &buffer_desc, + Some(b_id) + )); + b_id + } else { + warn!( + "No staging buffer available for ExternalImageId({:?})", + external_id + ); + continue; + }; + present_data.queued_buffer_ids.push(buffer_id); + } else { + warn!("Data not found for ExternalImageId({:?})", external_id); + continue; + } + } + + let buffer_size = + (size.height as u32 * buffer_stride) as wgt::BufferAddress; + let comm_desc = wgt::CommandEncoderDescriptor { label: None }; + let _ = gfx_select!(encoder_id => global.device_create_command_encoder( + device_id, + &comm_desc, + Some(encoder_id) + )); + + let buffer_cv = ImageCopyBuffer { + buffer: buffer_id, + layout: wgt::ImageDataLayout { + offset: 0, + bytes_per_row: Some(buffer_stride), + rows_per_image: None, + }, + }; + let texture_cv = ImageCopyTexture { + texture: texture_id, + mip_level: 0, + origin: wgt::Origin3d::ZERO, + aspect: wgt::TextureAspect::All, + }; + let copy_size = wgt::Extent3d { + width: size.width as u32, + height: size.height as u32, + depth_or_array_layers: 1, + }; + let _ = gfx_select!(encoder_id => global.command_encoder_copy_texture_to_buffer( + encoder_id, + &texture_cv, + &buffer_cv, + ©_size + )); + let _ = gfx_select!(encoder_id => global.command_encoder_finish( + encoder_id, + &wgt::CommandBufferDescriptor::default() + )); + let _ = gfx_select!(queue_id => global.queue_submit( + queue_id, + &[encoder_id.into_command_buffer_id()] + )); + + let glob = Arc::clone(&self.global); + let wgpu_image_map = Arc::clone(&self.wgpu_image_map); + let webrender_api = Arc::clone(&self.webrender_api); + let webrender_document = self.webrender_document; + let callback = BufferMapCallback::from_rust(Box::from(move |result| { + match result { + Ok(()) => { + let global = &glob; + let (slice_pointer, range_size) = gfx_select!(buffer_id => + global.buffer_get_mapped_range(buffer_id, 0, Some(buffer_size as u64))) + .unwrap(); + let data = unsafe { + slice::from_raw_parts(slice_pointer, range_size as usize) + } + .to_vec(); + if let Some(present_data) = + wgpu_image_map.lock().unwrap().get_mut(&external_id) + { + present_data.data = data; + let mut txn = Transaction::new(); + txn.update_image( + present_data.image_key, + present_data.image_desc, + present_data.image_data.clone(), + &DirtyRect::All, + ); + webrender_api + .lock() + .unwrap() + .send_transaction(webrender_document, txn); + present_data + .queued_buffer_ids + .retain(|b_id| *b_id != buffer_id); + present_data.available_buffer_ids.push(buffer_id); + } else { + warn!( + "Data not found for ExternalImageId({:?})", + external_id + ); + } + let _ = + gfx_select!(buffer_id => global.buffer_unmap(buffer_id)); + }, + _ => error!("Could not map buffer({:?})", buffer_id), + } + })); + let map_op = BufferMapOperation { + host: HostMap::Read, + callback: Some(callback), + }; + let _ = gfx_select!(buffer_id + => global.buffer_map_async(buffer_id, 0, Some(buffer_size), map_op)); + }, + WebGPURequest::UnmapBuffer { + buffer_id, + device_id, + array_buffer, + is_map_read, + offset, + size, + } => { + let global = &self.global; + if !is_map_read { + let (slice_pointer, range_size) = + gfx_select!(buffer_id => global.buffer_get_mapped_range( + buffer_id, + offset, + Some(size) + )) + .unwrap(); + unsafe { + slice::from_raw_parts_mut(slice_pointer, range_size as usize) + } + .copy_from_slice(&array_buffer); + } + let result = gfx_select!(buffer_id => global.buffer_unmap(buffer_id)); + self.send_result(device_id, scope_id, result); + }, + WebGPURequest::WriteBuffer { + queue_id, + buffer_id, + buffer_offset, + data, + } => { + let global = &self.global; + //TODO: Report result to content process + let result = gfx_select!(queue_id => global.queue_write_buffer( + queue_id, + buffer_id, + buffer_offset as wgt::BufferAddress, + &data + )); + self.send_result(queue_id.transmute(), scope_id, result); + }, + WebGPURequest::WriteTexture { + queue_id, + texture_cv, + data_layout, + size, + data, + } => { + let global = &self.global; + //TODO: Report result to content process + let result = gfx_select!(queue_id => global.queue_write_texture( + queue_id, + &texture_cv, + &data, + &data_layout, + &size + )); + self.send_result(queue_id.transmute(), scope_id, result); + }, + WebGPURequest::QueueOnSubmittedWorkDone { sender, queue_id } => { + let global = &self.global; + + let callback = SubmittedWorkDoneClosure::from_rust(Box::from(move || { + if let Err(e) = sender.send(Some(Ok(WebGPUResponse::SubmittedWorkDone))) + { + warn!("Could not send SubmittedWorkDone Response ({})", e); + } + })); + let result = gfx_select!(queue_id => global.queue_on_submitted_work_done(queue_id, callback)); + self.send_result(queue_id.transmute(), scope_id, result); + }, + WebGPURequest::DropTexture(id) => { + let global = &self.global; + gfx_select!(id => global.texture_drop(id, true)); + if let Err(e) = self.script_sender.send(WebGPUMsg::FreeTexture(id)) { + warn!("Unable to send FreeTexture({:?}) ({:?})", id, e); + }; + }, + WebGPURequest::DropAdapter(id) => { + let global = &self.global; + gfx_select!(id => global.adapter_drop(id)); + if let Err(e) = self.script_sender.send(WebGPUMsg::FreeAdapter(id)) { + warn!("Unable to send FreeAdapter({:?}) ({:?})", id, e); + }; + }, + WebGPURequest::DropBuffer(id) => { + let global = &self.global; + gfx_select!(id => global.buffer_drop(id, true)); + if let Err(e) = self.script_sender.send(WebGPUMsg::FreeBuffer(id)) { + warn!("Unable to send FreeBuffer({:?}) ({:?})", id, e); + }; + }, + WebGPURequest::DropPipelineLayout(id) => { + let global = &self.global; + gfx_select!(id => global.pipeline_layout_drop(id)); + if let Err(e) = self.script_sender.send(WebGPUMsg::FreePipelineLayout(id)) { + warn!("Unable to send FreePipelineLayout({:?}) ({:?})", id, e); + }; + }, + WebGPURequest::DropComputePipeline(id) => { + let global = &self.global; + gfx_select!(id => global.compute_pipeline_drop(id)); + if let Err(e) = self.script_sender.send(WebGPUMsg::FreeComputePipeline(id)) + { + warn!("Unable to send FreeComputePipeline({:?}) ({:?})", id, e); + }; + }, + WebGPURequest::DropRenderPipeline(id) => { + let global = &self.global; + gfx_select!(id => global.render_pipeline_drop(id)); + if let Err(e) = self.script_sender.send(WebGPUMsg::FreeRenderPipeline(id)) { + warn!("Unable to send FreeRenderPipeline({:?}) ({:?})", id, e); + }; + }, + WebGPURequest::DropBindGroup(id) => { + let global = &self.global; + gfx_select!(id => global.bind_group_drop(id)); + if let Err(e) = self.script_sender.send(WebGPUMsg::FreeBindGroup(id)) { + warn!("Unable to send FreeBindGroup({:?}) ({:?})", id, e); + }; + }, + WebGPURequest::DropBindGroupLayout(id) => { + let global = &self.global; + gfx_select!(id => global.bind_group_layout_drop(id)); + if let Err(e) = self.script_sender.send(WebGPUMsg::FreeBindGroupLayout(id)) + { + warn!("Unable to send FreeBindGroupLayout({:?}) ({:?})", id, e); + }; + }, + WebGPURequest::DropTextureView(id) => { + let global = &self.global; + let _result = gfx_select!(id => global.texture_view_drop(id, true)); + if let Err(e) = self.script_sender.send(WebGPUMsg::FreeTextureView(id)) { + warn!("Unable to send FreeTextureView({:?}) ({:?})", id, e); + }; + }, + WebGPURequest::DropSampler(id) => { + let global = &self.global; + gfx_select!(id => global.sampler_drop(id)); + if let Err(e) = self.script_sender.send(WebGPUMsg::FreeSampler(id)) { + warn!("Unable to send FreeSampler({:?}) ({:?})", id, e); + }; + }, + WebGPURequest::DropShaderModule(id) => { + let global = &self.global; + gfx_select!(id => global.shader_module_drop(id)); + if let Err(e) = self.script_sender.send(WebGPUMsg::FreeShaderModule(id)) { + warn!("Unable to send FreeShaderModule({:?}) ({:?})", id, e); + }; + }, + WebGPURequest::DropRenderBundle(id) => { + let global = &self.global; + gfx_select!(id => global.render_bundle_drop(id)); + if let Err(e) = self.script_sender.send(WebGPUMsg::FreeRenderBundle(id)) { + warn!("Unable to send FreeRenderBundle({:?}) ({:?})", id, e); + }; + }, + WebGPURequest::DropQuerySet(id) => { + let global = &self.global; + gfx_select!(id => global.query_set_drop(id)); + if let Err(e) = self.script_sender.send(WebGPUMsg::FreeQuerySet(id)) { + warn!("Unable to send FreeQuerySet({:?}) ({:?})", id, e); + }; + }, + } + } + } + if let Err(e) = self.script_sender.send(WebGPUMsg::Exit) { + warn!("Failed to send WebGPUMsg::Exit to script ({})", e); + } + } + + fn send_result<U, T: std::fmt::Debug>( + &self, + device_id: id::DeviceId, + scope_id: Option<ErrorScopeId>, + result: Result<U, T>, + ) { + let &pipeline_id = self.devices.get(&WebGPUDevice(device_id)).unwrap(); + if let Err(w) = self.script_sender.send(WebGPUMsg::WebGPUOpResult { + device: WebGPUDevice(device_id), + scope_id, + pipeline_id, + result: if let Err(e) = result { + let err = format!("{:?}", e); + if err.contains("OutOfMemory") { + WebGPUOpResult::OutOfMemoryError + } else { + WebGPUOpResult::ValidationError(err) + } + } else { + WebGPUOpResult::Success + }, + }) { + warn!("Failed to send WebGPUOpResult ({})", w); + } + } + + fn encoder_record_error<U, T: std::fmt::Debug>( + &self, + encoder_id: id::CommandEncoderId, + result: &Result<U, T>, + ) { + if let Err(ref e) = result { + self.error_command_encoders + .borrow_mut() + .entry(encoder_id) + .or_insert_with(|| format!("{:?}", e)); + } + } +} + +fn tuple_to_result<T, E>(res: (T, Option<E>)) -> Result<T, E> { + if let Some(err) = res.1 { + Err(err) + } else { + Ok(res.0) + } +} |