mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
1378 lines
60 KiB
Rust
1378 lines
60 KiB
Rust
/* 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/. */
|
|
|
|
#[macro_use]
|
|
extern crate log;
|
|
#[macro_use]
|
|
pub extern crate wgpu_core as wgpu;
|
|
pub extern crate wgpu_types as wgt;
|
|
|
|
pub mod identity;
|
|
|
|
use arrayvec::ArrayVec;
|
|
use euclid::default::Size2D;
|
|
use identity::{IdentityRecyclerFactory, WebGPUMsg, WebGPUOpResult};
|
|
use ipc_channel::ipc::{self, IpcReceiver, IpcSender, IpcSharedMemory};
|
|
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
|
|
use msg::constellation_msg::PipelineId;
|
|
use serde::{Deserialize, Serialize};
|
|
use servo_config::pref;
|
|
use smallvec::SmallVec;
|
|
use std::borrow::Cow;
|
|
use std::cell::RefCell;
|
|
use std::collections::HashMap;
|
|
use std::num::NonZeroU64;
|
|
use std::rc::Rc;
|
|
use std::slice;
|
|
use std::sync::{Arc, Mutex};
|
|
use std::time::{Duration, Instant};
|
|
use webrender_traits::{
|
|
WebrenderExternalImageApi, WebrenderExternalImageRegistry, WebrenderImageHandlerType,
|
|
WebrenderImageSource,
|
|
};
|
|
use wgpu::{
|
|
binding_model::{BindGroupDescriptor, BindGroupLayoutDescriptor, PipelineLayoutDescriptor},
|
|
command::{
|
|
BufferCopyView, ComputePass, RenderBundleDescriptor, RenderBundleEncoder, RenderPass,
|
|
TextureCopyView,
|
|
},
|
|
device::{HostMap, ImplicitPipelineIds},
|
|
id,
|
|
instance::RequestAdapterOptions,
|
|
pipeline::{ComputePipelineDescriptor, RenderPipelineDescriptor},
|
|
resource::{
|
|
BufferDescriptor, BufferMapAsyncStatus, BufferMapOperation, SamplerDescriptor,
|
|
TextureDescriptor, TextureViewDescriptor,
|
|
},
|
|
};
|
|
|
|
pub type ErrorScopeId = NonZeroU64;
|
|
const DEVICE_POLL_INTERVAL: u64 = 100;
|
|
pub const PRESENTATION_BUFFER_COUNT: usize = 10;
|
|
|
|
#[derive(Debug, Deserialize, Serialize)]
|
|
pub enum WebGPUResponse {
|
|
RequestAdapter {
|
|
adapter_name: String,
|
|
adapter_id: WebGPUAdapter,
|
|
channel: WebGPU,
|
|
},
|
|
RequestDevice {
|
|
device_id: WebGPUDevice,
|
|
queue_id: WebGPUQueue,
|
|
descriptor: wgt::DeviceDescriptor,
|
|
label: Option<String>,
|
|
},
|
|
BufferMapAsync(IpcSharedMemory),
|
|
}
|
|
|
|
pub type WebGPUResponseResult = Result<WebGPUResponse, String>;
|
|
|
|
#[derive(Debug, Deserialize, Serialize)]
|
|
pub enum WebGPURequest {
|
|
BufferMapAsync {
|
|
sender: IpcSender<WebGPUResponseResult>,
|
|
buffer_id: id::BufferId,
|
|
device_id: id::DeviceId,
|
|
host_map: HostMap,
|
|
map_range: std::ops::Range<u64>,
|
|
},
|
|
BufferMapComplete(id::BufferId),
|
|
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: BufferCopyView,
|
|
destination: TextureCopyView,
|
|
copy_size: wgt::Extent3d,
|
|
},
|
|
CopyTextureToBuffer {
|
|
command_encoder_id: id::CommandEncoderId,
|
|
source: TextureCopyView,
|
|
destination: BufferCopyView,
|
|
copy_size: wgt::Extent3d,
|
|
},
|
|
CopyTextureToTexture {
|
|
command_encoder_id: id::CommandEncoderId,
|
|
source: TextureCopyView,
|
|
destination: TextureCopyView,
|
|
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<webrender_api::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: Vec<u32>,
|
|
},
|
|
CreateSwapChain {
|
|
device_id: id::DeviceId,
|
|
buffer_ids: ArrayVec<id::BufferId, PRESENTATION_BUFFER_COUNT>,
|
|
external_id: u64,
|
|
sender: IpcSender<webrender_api::ImageKey>,
|
|
image_desc: webrender_api::ImageDescriptor,
|
|
image_data: webrender_api::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),
|
|
DestroySwapChain {
|
|
external_id: u64,
|
|
image_key: webrender_api::ImageKey,
|
|
},
|
|
DestroyTexture(id::TextureId),
|
|
Exit(IpcSender<()>),
|
|
FreeCommandBuffer(id::CommandBufferId),
|
|
FreeDevice(id::DeviceId),
|
|
RenderBundleEncoderFinish {
|
|
render_bundle_encoder: RenderBundleEncoder,
|
|
descriptor: RenderBundleDescriptor<'static>,
|
|
render_bundle_id: id::RenderBundleId,
|
|
device_id: id::DeviceId,
|
|
},
|
|
RequestAdapter {
|
|
sender: IpcSender<WebGPUResponseResult>,
|
|
options: RequestAdapterOptions,
|
|
ids: SmallVec<[id::AdapterId; 4]>,
|
|
},
|
|
RequestDevice {
|
|
sender: IpcSender<WebGPUResponseResult>,
|
|
adapter_id: WebGPUAdapter,
|
|
descriptor: wgt::DeviceDescriptor,
|
|
device_id: id::DeviceId,
|
|
pipeline_id: PipelineId,
|
|
label: Option<String>,
|
|
},
|
|
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,
|
|
},
|
|
UpdateWebRenderData {
|
|
buffer_id: id::BufferId,
|
|
external_id: u64,
|
|
buffer_size: usize,
|
|
},
|
|
WriteBuffer {
|
|
queue_id: id::QueueId,
|
|
buffer_id: id::BufferId,
|
|
buffer_offset: u64,
|
|
data: IpcSharedMemory,
|
|
},
|
|
WriteTexture {
|
|
queue_id: id::QueueId,
|
|
texture_cv: TextureCopyView,
|
|
data_layout: wgt::TextureDataLayout,
|
|
size: wgt::Extent3d,
|
|
data: IpcSharedMemory,
|
|
},
|
|
}
|
|
|
|
struct BufferMapInfo<'a, T> {
|
|
buffer_id: id::BufferId,
|
|
sender: IpcSender<T>,
|
|
global: &'a wgpu::hub::Global<IdentityRecyclerFactory>,
|
|
size: usize,
|
|
external_id: Option<u64>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
|
pub struct WebGPU(pub IpcSender<(Option<ErrorScopeId>, WebGPURequest)>);
|
|
|
|
impl WebGPU {
|
|
pub fn new(
|
|
webrender_api_sender: webrender_api::RenderApiSender,
|
|
webrender_document: webrender_api::DocumentId,
|
|
external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
|
|
wgpu_image_map: Arc<Mutex<HashMap<u64, PresentationData>>>,
|
|
) -> Option<(Self, IpcReceiver<WebGPUMsg>)> {
|
|
if !pref!(dom.webgpu.enabled) {
|
|
return None;
|
|
}
|
|
let (sender, receiver) = match ipc::channel() {
|
|
Ok(sender_and_receiver) => sender_and_receiver,
|
|
Err(e) => {
|
|
warn!(
|
|
"Failed to create sender and receiver for WGPU thread ({})",
|
|
e
|
|
);
|
|
return None;
|
|
},
|
|
};
|
|
let sender_clone = sender.clone();
|
|
|
|
let (script_sender, script_recv) = match ipc::channel() {
|
|
Ok(sender_and_receiver) => sender_and_receiver,
|
|
Err(e) => {
|
|
warn!(
|
|
"Failed to create receiver and sender for WGPU thread ({})",
|
|
e
|
|
);
|
|
return None;
|
|
},
|
|
};
|
|
|
|
if let Err(e) = std::thread::Builder::new()
|
|
.name("WGPU".to_owned())
|
|
.spawn(move || {
|
|
WGPU::new(
|
|
receiver,
|
|
sender_clone,
|
|
script_sender,
|
|
webrender_api_sender,
|
|
webrender_document,
|
|
external_images,
|
|
wgpu_image_map,
|
|
)
|
|
.run();
|
|
})
|
|
{
|
|
warn!("Failed to spwan WGPU thread ({})", e);
|
|
return None;
|
|
}
|
|
Some((WebGPU(sender), script_recv))
|
|
}
|
|
|
|
pub fn exit(&self, sender: IpcSender<()>) -> Result<(), &'static str> {
|
|
self.0
|
|
.send((None, WebGPURequest::Exit(sender)))
|
|
.map_err(|_| "Failed to send Exit message")
|
|
}
|
|
}
|
|
|
|
struct WGPU<'a> {
|
|
receiver: IpcReceiver<(Option<ErrorScopeId>, WebGPURequest)>,
|
|
sender: IpcSender<(Option<ErrorScopeId>, WebGPURequest)>,
|
|
script_sender: IpcSender<WebGPUMsg>,
|
|
global: wgpu::hub::Global<IdentityRecyclerFactory>,
|
|
adapters: Vec<WebGPUAdapter>,
|
|
devices: HashMap<WebGPUDevice, PipelineId>,
|
|
// Track invalid adapters https://gpuweb.github.io/gpuweb/#invalid
|
|
_invalid_adapters: Vec<WebGPUAdapter>,
|
|
// Buffers with pending mapping
|
|
buffer_maps: HashMap<id::BufferId, Rc<BufferMapInfo<'a, WebGPUResponseResult>>>,
|
|
// Presentation Buffers with pending mapping
|
|
present_buffer_maps:
|
|
HashMap<id::BufferId, Rc<BufferMapInfo<'a, (Option<ErrorScopeId>, WebGPURequest)>>>,
|
|
//TODO: Remove this (https://github.com/gfx-rs/wgpu/issues/867)
|
|
error_command_encoders: RefCell<HashMap<id::CommandEncoderId, String>>,
|
|
webrender_api: webrender_api::RenderApi,
|
|
webrender_document: webrender_api::DocumentId,
|
|
external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
|
|
wgpu_image_map: Arc<Mutex<HashMap<u64, PresentationData>>>,
|
|
last_poll: Instant,
|
|
}
|
|
|
|
impl<'a> WGPU<'a> {
|
|
fn new(
|
|
receiver: IpcReceiver<(Option<ErrorScopeId>, WebGPURequest)>,
|
|
sender: IpcSender<(Option<ErrorScopeId>, WebGPURequest)>,
|
|
script_sender: IpcSender<WebGPUMsg>,
|
|
webrender_api_sender: webrender_api::RenderApiSender,
|
|
webrender_document: webrender_api::DocumentId,
|
|
external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
|
|
wgpu_image_map: Arc<Mutex<HashMap<u64, PresentationData>>>,
|
|
) -> Self {
|
|
let factory = IdentityRecyclerFactory {
|
|
sender: script_sender.clone(),
|
|
self_sender: sender.clone(),
|
|
};
|
|
WGPU {
|
|
receiver,
|
|
sender,
|
|
script_sender,
|
|
global: wgpu::hub::Global::new("wgpu-core", factory, wgt::BackendBit::PRIMARY),
|
|
adapters: Vec::new(),
|
|
devices: HashMap::new(),
|
|
_invalid_adapters: Vec::new(),
|
|
buffer_maps: HashMap::new(),
|
|
present_buffer_maps: HashMap::new(),
|
|
error_command_encoders: RefCell::new(HashMap::new()),
|
|
webrender_api: webrender_api_sender.create_api(),
|
|
webrender_document,
|
|
external_images,
|
|
wgpu_image_map,
|
|
last_poll: Instant::now(),
|
|
}
|
|
}
|
|
|
|
fn run(&'a mut self) {
|
|
loop {
|
|
if self.last_poll.elapsed() >= Duration::from_millis(DEVICE_POLL_INTERVAL) {
|
|
let _ = self.global.poll_all_devices(false);
|
|
self.last_poll = Instant::now();
|
|
}
|
|
if let Ok((scope_id, msg)) = self.receiver.try_recv() {
|
|
match msg {
|
|
WebGPURequest::BufferMapAsync {
|
|
sender,
|
|
buffer_id,
|
|
device_id,
|
|
host_map,
|
|
map_range,
|
|
} => {
|
|
let map_info = BufferMapInfo {
|
|
buffer_id,
|
|
sender: sender.clone(),
|
|
global: &self.global,
|
|
size: (map_range.end - map_range.start) as usize,
|
|
external_id: None,
|
|
};
|
|
self.buffer_maps.insert(buffer_id, Rc::new(map_info));
|
|
|
|
unsafe extern "C" fn callback(
|
|
status: BufferMapAsyncStatus,
|
|
userdata: *mut u8,
|
|
) {
|
|
let info = Rc::from_raw(
|
|
userdata as *const BufferMapInfo<WebGPUResponseResult>,
|
|
);
|
|
let msg = match status {
|
|
BufferMapAsyncStatus::Success => {
|
|
let global = &info.global;
|
|
let data_pt = gfx_select!(info.buffer_id =>
|
|
global.buffer_get_mapped_range(info.buffer_id, 0, None))
|
|
.unwrap();
|
|
let data = slice::from_raw_parts(data_pt, info.size);
|
|
Ok(WebGPUResponse::BufferMapAsync(IpcSharedMemory::from_bytes(
|
|
data,
|
|
)))
|
|
},
|
|
_ => {
|
|
warn!("Could not map buffer({:?})", info.buffer_id);
|
|
Err(String::from("Failed to map Buffer"))
|
|
},
|
|
};
|
|
if let Err(e) = info.sender.send(msg) {
|
|
warn!("Could not send BufferMapAsync Response ({})", e);
|
|
}
|
|
}
|
|
|
|
let operation = BufferMapOperation {
|
|
host: host_map,
|
|
callback,
|
|
user_data: convert_to_pointer(
|
|
self.buffer_maps.get(&buffer_id).unwrap().clone(),
|
|
),
|
|
};
|
|
let global = &self.global;
|
|
let result = gfx_select!(buffer_id => global.buffer_map_async(buffer_id, map_range, operation));
|
|
if let Err(ref e) = result {
|
|
if let Err(w) = sender.send(Err(format!("{:?}", e))) {
|
|
warn!("Failed to send BufferMapAsync Response ({:?})", w);
|
|
}
|
|
}
|
|
self.send_result(device_id, scope_id, result);
|
|
},
|
|
WebGPURequest::BufferMapComplete(buffer_id) => {
|
|
self.buffer_maps.remove(&buffer_id);
|
|
},
|
|
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 {
|
|
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 = gfx_select!(bind_group_id =>
|
|
global.device_create_bind_group(device_id, &descriptor, bind_group_id));
|
|
if result.is_err() {
|
|
let _ = gfx_select!(bind_group_id => global.bind_group_error(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 = gfx_select!(bind_group_layout_id =>
|
|
global.device_create_bind_group_layout(device_id, &desc, bind_group_layout_id));
|
|
if result.is_err() {
|
|
let _ = gfx_select!(bind_group_layout_id =>
|
|
global.bind_group_layout_error(bind_group_layout_id));
|
|
}
|
|
self.send_result(device_id, scope_id, result);
|
|
} else {
|
|
let _ = gfx_select!(bind_group_layout_id =>
|
|
global.bind_group_layout_error(bind_group_layout_id));
|
|
}
|
|
},
|
|
WebGPURequest::CreateBuffer {
|
|
device_id,
|
|
buffer_id,
|
|
descriptor,
|
|
} => {
|
|
let global = &self.global;
|
|
if let Some(desc) = descriptor {
|
|
let result = gfx_select!(buffer_id =>
|
|
global.device_create_buffer(device_id, &desc, buffer_id));
|
|
if result.is_err() {
|
|
let _ = gfx_select!(buffer_id => global.buffer_error(buffer_id));
|
|
}
|
|
self.send_result(device_id, scope_id, result);
|
|
} else {
|
|
let _ = gfx_select!(buffer_id => global.buffer_error(buffer_id));
|
|
}
|
|
},
|
|
WebGPURequest::CreateCommandEncoder {
|
|
device_id,
|
|
command_encoder_id,
|
|
label,
|
|
} => {
|
|
let global = &self.global;
|
|
let desc = wgt::CommandEncoderDescriptor { label };
|
|
let result = gfx_select!(command_encoder_id =>
|
|
global.device_create_command_encoder(device_id, &desc, command_encoder_id));
|
|
if result.is_err() {
|
|
let _ = gfx_select!(command_encoder_id => global.command_encoder_error(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.clone());
|
|
let implicit =
|
|
implicit_ids
|
|
.as_ref()
|
|
.map(|(layout, _)| ImplicitPipelineIds {
|
|
root_id: *layout,
|
|
group_ids: bgls.as_slice(),
|
|
});
|
|
let result = gfx_select!(compute_pipeline_id => global.device_create_compute_pipeline(
|
|
device_id,
|
|
&descriptor,
|
|
compute_pipeline_id,
|
|
implicit
|
|
));
|
|
if result.is_err() {
|
|
let _ = gfx_select!(compute_pipeline_id =>
|
|
global.compute_pipeline_error(compute_pipeline_id));
|
|
if let Some((layout, bgls)) = implicit_ids {
|
|
let _ = gfx_select!(layout => global.pipeline_layout_error(layout));
|
|
bgls.iter().for_each(|&bgl| {
|
|
let _ = gfx_select!(bgl => global.bind_group_layout_error(bgl));
|
|
});
|
|
}
|
|
}
|
|
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 = gfx_select!(pipeline_layout_id =>
|
|
global.device_create_pipeline_layout(device_id, &descriptor, pipeline_layout_id));
|
|
if result.is_err() {
|
|
let _ = gfx_select!(pipeline_layout_id => global.pipeline_layout_error(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.clone());
|
|
let implicit =
|
|
implicit_ids
|
|
.as_ref()
|
|
.map(|(layout, _)| ImplicitPipelineIds {
|
|
root_id: *layout,
|
|
group_ids: bgls.as_slice(),
|
|
});
|
|
if let Some(desc) = descriptor {
|
|
let result = gfx_select!(render_pipeline_id =>
|
|
global.device_create_render_pipeline(device_id, &desc, render_pipeline_id, implicit));
|
|
if result.is_err() {
|
|
let _ = gfx_select!(render_pipeline_id =>
|
|
global.render_pipeline_error(render_pipeline_id));
|
|
if let Some((layout, bgls)) = implicit_ids {
|
|
let _ =
|
|
gfx_select!(layout => global.pipeline_layout_error(layout));
|
|
bgls.iter().for_each(|&bgl| {
|
|
let _ = gfx_select!(bgl => global.bind_group_layout_error(bgl));
|
|
});
|
|
}
|
|
}
|
|
self.send_result(device_id, scope_id, result);
|
|
} else {
|
|
let _ = gfx_select!(render_pipeline_id => global.render_pipeline_error(render_pipeline_id));
|
|
}
|
|
},
|
|
WebGPURequest::CreateSampler {
|
|
device_id,
|
|
sampler_id,
|
|
descriptor,
|
|
} => {
|
|
let global = &self.global;
|
|
let result = gfx_select!(sampler_id => global.device_create_sampler(
|
|
device_id,
|
|
&descriptor,
|
|
sampler_id
|
|
));
|
|
if result.is_err() {
|
|
let _ = gfx_select!(sampler_id => global.sampler_error(sampler_id));
|
|
}
|
|
self.send_result(device_id, scope_id, result);
|
|
},
|
|
WebGPURequest::CreateShaderModule {
|
|
device_id,
|
|
program_id,
|
|
program,
|
|
} => {
|
|
let global = &self.global;
|
|
let source =
|
|
wgpu_core::pipeline::ShaderModuleSource::SpirV(Cow::Owned(program));
|
|
let result = gfx_select!(program_id =>
|
|
global.device_create_shader_module(device_id, source, program_id));
|
|
if result.is_err() {
|
|
let _ =
|
|
gfx_select!(program_id => global.shader_module_error(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 image_key = self.webrender_api.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,
|
|
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 = webrender_api::Transaction::new();
|
|
txn.add_image(image_key, image_desc, image_data, None);
|
|
self.webrender_api
|
|
.send_transaction(self.webrender_document, txn);
|
|
},
|
|
WebGPURequest::CreateTexture {
|
|
device_id,
|
|
texture_id,
|
|
descriptor,
|
|
} => {
|
|
let global = &self.global;
|
|
if let Some(desc) = descriptor {
|
|
let result = gfx_select!(texture_id => global.device_create_texture(
|
|
device_id,
|
|
&desc,
|
|
texture_id
|
|
));
|
|
if result.is_err() {
|
|
let _ = gfx_select!(texture_id => global.texture_error(texture_id));
|
|
}
|
|
self.send_result(device_id, scope_id, result);
|
|
} else {
|
|
let _ = gfx_select!(texture_id => global.texture_error(texture_id));
|
|
}
|
|
},
|
|
WebGPURequest::CreateTextureView {
|
|
texture_id,
|
|
texture_view_id,
|
|
device_id,
|
|
descriptor,
|
|
} => {
|
|
let global = &self.global;
|
|
if let Some(desc) = descriptor {
|
|
let result = gfx_select!(texture_view_id => global.texture_create_view(
|
|
texture_id,
|
|
&desc,
|
|
texture_view_id
|
|
));
|
|
if result.is_err() {
|
|
let _ = gfx_select!(texture_view_id => global.texture_view_error(texture_view_id));
|
|
}
|
|
self.send_result(device_id, scope_id, result);
|
|
} else {
|
|
let _ = gfx_select!(texture_view_id => global.texture_view_error(texture_view_id));
|
|
}
|
|
},
|
|
WebGPURequest::DestroyBuffer(buffer) => {
|
|
let global = &self.global;
|
|
gfx_select!(buffer => global.buffer_drop(buffer, false));
|
|
},
|
|
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 = webrender_api::Transaction::new();
|
|
txn.delete_image(image_key);
|
|
self.webrender_api
|
|
.send_transaction(self.webrender_document, txn);
|
|
},
|
|
WebGPURequest::DestroyTexture(texture) => {
|
|
let global = &self.global;
|
|
gfx_select!(texture => global.texture_drop(texture));
|
|
},
|
|
WebGPURequest::Exit(sender) => {
|
|
if let Err(e) = self.script_sender.send(WebGPUMsg::Exit) {
|
|
warn!("Failed to send WebGPUMsg::Exit to script ({})", e);
|
|
}
|
|
if let Err(e) = sender.send(()) {
|
|
warn!("Failed to send response to WebGPURequest::Exit ({})", e)
|
|
}
|
|
return;
|
|
},
|
|
WebGPURequest::FreeCommandBuffer(command_buffer_id) => {
|
|
self.error_command_encoders
|
|
.borrow_mut()
|
|
.remove(&command_buffer_id);
|
|
},
|
|
WebGPURequest::FreeDevice(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);
|
|
}
|
|
},
|
|
WebGPURequest::RenderBundleEncoderFinish {
|
|
render_bundle_encoder,
|
|
descriptor,
|
|
render_bundle_id,
|
|
device_id,
|
|
} => {
|
|
let global = &self.global;
|
|
let result = gfx_select!(render_bundle_id => global.render_bundle_encoder_finish(
|
|
render_bundle_encoder,
|
|
&descriptor,
|
|
render_bundle_id
|
|
));
|
|
if result.is_err() {
|
|
let _ = gfx_select!(render_bundle_id => global.render_bundle_error(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, |id| id.backend()),
|
|
) {
|
|
Ok(id) => id,
|
|
Err(w) => {
|
|
if let Err(e) = sender.send(Err(format!("{:?}", w))) {
|
|
warn!(
|
|
"Failed to send response to WebGPURequest::RequestAdapter ({})",
|
|
e
|
|
)
|
|
}
|
|
return;
|
|
},
|
|
};
|
|
let adapter = WebGPUAdapter(adapter_id);
|
|
self.adapters.push(adapter);
|
|
let global = &self.global;
|
|
let info =
|
|
gfx_select!(adapter_id => global.adapter_get_info(adapter_id)).unwrap();
|
|
if let Err(e) = sender.send(Ok(WebGPUResponse::RequestAdapter {
|
|
adapter_name: info.name,
|
|
adapter_id: adapter,
|
|
channel: WebGPU(self.sender.clone()),
|
|
})) {
|
|
warn!(
|
|
"Failed to send response to WebGPURequest::RequestAdapter ({})",
|
|
e
|
|
)
|
|
}
|
|
},
|
|
WebGPURequest::RequestDevice {
|
|
sender,
|
|
adapter_id,
|
|
descriptor,
|
|
device_id,
|
|
pipeline_id,
|
|
label,
|
|
} => {
|
|
let global = &self.global;
|
|
let id = match gfx_select!(device_id => global.adapter_request_device(
|
|
adapter_id.0,
|
|
&descriptor,
|
|
None,
|
|
device_id
|
|
)) {
|
|
Ok(id) => id,
|
|
Err(e) => {
|
|
let _ = gfx_select!(device_id => global.device_error(device_id));
|
|
if let Err(w) = sender.send(Err(format!("{:?}", e))) {
|
|
warn!(
|
|
"Failed to send response to WebGPURequest::RequestDevice ({})",
|
|
w
|
|
)
|
|
}
|
|
return;
|
|
},
|
|
};
|
|
let device = WebGPUDevice(id);
|
|
// Note: (zakorgy) Note sure if sending the queue is needed at all,
|
|
// since wgpu-core uses the same id for the device and the queue
|
|
let queue = WebGPUQueue(id);
|
|
self.devices.insert(device, pipeline_id);
|
|
if let Err(e) = sender.send(Ok(WebGPUResponse::RequestDevice {
|
|
device_id: device,
|
|
queue_id: queue,
|
|
descriptor,
|
|
label,
|
|
})) {
|
|
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));
|
|
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, 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::BufferUsage::MAP_READ |
|
|
wgt::BufferUsage::COPY_DST,
|
|
mapped_at_creation: false,
|
|
};
|
|
let _ = gfx_select!(b_id => global.device_create_buffer(
|
|
device_id,
|
|
&buffer_desc,
|
|
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,
|
|
encoder_id
|
|
));
|
|
|
|
let buffer_cv = BufferCopyView {
|
|
buffer: buffer_id,
|
|
layout: wgt::TextureDataLayout {
|
|
offset: 0,
|
|
bytes_per_row: buffer_stride,
|
|
rows_per_image: 0,
|
|
},
|
|
};
|
|
let texture_cv = TextureCopyView {
|
|
texture: texture_id,
|
|
mip_level: 0,
|
|
origin: wgt::Origin3d::ZERO,
|
|
};
|
|
let copy_size = wgt::Extent3d {
|
|
width: size.width as u32,
|
|
height: size.height as u32,
|
|
depth: 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]
|
|
));
|
|
|
|
let map_info = BufferMapInfo {
|
|
buffer_id,
|
|
sender: self.sender.clone(),
|
|
global: &self.global,
|
|
size: buffer_size as usize,
|
|
external_id: Some(external_id),
|
|
};
|
|
self.present_buffer_maps
|
|
.insert(buffer_id, Rc::new(map_info));
|
|
unsafe extern "C" fn callback(
|
|
status: BufferMapAsyncStatus,
|
|
userdata: *mut u8,
|
|
) {
|
|
let info = Rc::from_raw(
|
|
userdata
|
|
as *const BufferMapInfo<(Option<ErrorScopeId>, WebGPURequest)>,
|
|
);
|
|
match status {
|
|
BufferMapAsyncStatus::Success => {
|
|
if let Err(e) = info.sender.send((
|
|
None,
|
|
WebGPURequest::UpdateWebRenderData {
|
|
buffer_id: info.buffer_id,
|
|
buffer_size: info.size,
|
|
external_id: info.external_id.unwrap(),
|
|
},
|
|
)) {
|
|
warn!("Could not send UpdateWebRenderData ({})", e);
|
|
}
|
|
},
|
|
_ => error!("Could not map buffer({:?})", info.buffer_id),
|
|
}
|
|
}
|
|
let map_op = BufferMapOperation {
|
|
host: HostMap::Read,
|
|
callback,
|
|
user_data: convert_to_pointer(
|
|
self.present_buffer_maps.get(&buffer_id).unwrap().clone(),
|
|
),
|
|
};
|
|
let _ = gfx_select!(buffer_id => global.buffer_map_async(buffer_id, 0..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 map_ptr = gfx_select!(buffer_id => global.buffer_get_mapped_range(
|
|
buffer_id,
|
|
offset,
|
|
wgt::BufferSize::new(size)
|
|
))
|
|
.unwrap();
|
|
unsafe { slice::from_raw_parts_mut(map_ptr, 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::UpdateWebRenderData {
|
|
buffer_id,
|
|
buffer_size,
|
|
external_id,
|
|
} => {
|
|
let global = &self.global;
|
|
let data_pt = gfx_select!(buffer_id =>
|
|
global.buffer_get_mapped_range(buffer_id, 0, None))
|
|
.unwrap();
|
|
let data = unsafe { slice::from_raw_parts(data_pt, buffer_size) }.to_vec();
|
|
if let Some(present_data) =
|
|
self.wgpu_image_map.lock().unwrap().get_mut(&external_id)
|
|
{
|
|
present_data.data = data;
|
|
let mut txn = webrender_api::Transaction::new();
|
|
txn.update_image(
|
|
present_data.image_key,
|
|
present_data.image_desc,
|
|
present_data.image_data.clone(),
|
|
&webrender_api::DirtyRect::All,
|
|
);
|
|
self.webrender_api
|
|
.send_transaction(self.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));
|
|
self.present_buffer_maps.remove(&buffer_id);
|
|
},
|
|
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, 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, scope_id, result);
|
|
},
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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 convert_to_pointer<T: Sized>(obj: Rc<T>) -> *mut u8 {
|
|
Rc::into_raw(obj) as *mut u8
|
|
}
|
|
|
|
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!(WebGPUSwapChain, id::SwapChainId);
|
|
webgpu_resource!(WebGPUTexture, id::TextureId);
|
|
webgpu_resource!(WebGPUTextureView, id::TextureViewId);
|
|
|
|
pub struct WGPUExternalImages {
|
|
pub images: Arc<Mutex<HashMap<u64, PresentationData>>>,
|
|
pub locked_ids: HashMap<u64, Vec<u8>>,
|
|
}
|
|
|
|
impl WGPUExternalImages {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
images: Arc::new(Mutex::new(HashMap::new())),
|
|
locked_ids: HashMap::new(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl WebrenderExternalImageApi for WGPUExternalImages {
|
|
fn lock(&mut self, id: u64) -> (WebrenderImageSource, Size2D<i32>) {
|
|
let size;
|
|
let data;
|
|
if let Some(present_data) = self.images.lock().unwrap().get(&id) {
|
|
size = present_data.size;
|
|
data = present_data.data.clone();
|
|
} else {
|
|
size = Size2D::new(0, 0);
|
|
data = Vec::new();
|
|
}
|
|
let _ = self.locked_ids.insert(id, data);
|
|
(
|
|
WebrenderImageSource::Raw(self.locked_ids.get(&id).unwrap().as_slice()),
|
|
size,
|
|
)
|
|
}
|
|
|
|
fn unlock(&mut self, id: u64) {
|
|
let _ = self.locked_ids.remove(&id);
|
|
}
|
|
}
|
|
|
|
pub struct PresentationData {
|
|
device_id: id::DeviceId,
|
|
queue_id: id::QueueId,
|
|
pub data: Vec<u8>,
|
|
pub size: Size2D<i32>,
|
|
unassigned_buffer_ids: ArrayVec<id::BufferId, PRESENTATION_BUFFER_COUNT>,
|
|
available_buffer_ids: ArrayVec<id::BufferId, PRESENTATION_BUFFER_COUNT>,
|
|
queued_buffer_ids: ArrayVec<id::BufferId, PRESENTATION_BUFFER_COUNT>,
|
|
buffer_stride: u32,
|
|
image_key: webrender_api::ImageKey,
|
|
image_desc: webrender_api::ImageDescriptor,
|
|
image_data: webrender_api::ImageData,
|
|
}
|