webgpu: renovate gpucanvascontext and webgpu presentation to match the spec (#33521)

* Reimpl gpucanvascontext

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>

* ValidateTextureDescriptorAndCreateSwapChain

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>

* reconfigure

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>

* resize

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>

* work around deadlocks in wgpu core

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>

* match spec even more by moving all swapchain operations into one updatecontext

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>

* error handling

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>

* enable one test

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>

* label dummy texture

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>

* update expect

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>

* clean some expectation (they are not flaky anymore)

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>

* one more

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>

* change for configuration change in update_wr_image

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>

* DEFAULT_IMAGE_FORMAT

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>

* fixup

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>

* introduce WebGPUImageDescriptor

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>

---------

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
This commit is contained in:
Samson 2024-10-01 12:03:11 +02:00 committed by GitHub
parent 0b2549f4cb
commit 05ecb8eddb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
30 changed files with 795 additions and 483 deletions

View file

@ -2951,7 +2951,7 @@ impl Document {
self.dirty_webgpu_contexts self.dirty_webgpu_contexts
.borrow_mut() .borrow_mut()
.drain() .drain()
.for_each(|(_, context)| context.send_swap_chain_present()); .for_each(|(_, context)| context.update_rendering_of_webgpu_canvas());
} }
pub fn id_map(&self) -> Ref<HashMapTracedValues<Atom, Vec<Dom<Element>>>> { pub fn id_map(&self) -> Ref<HashMapTracedValues<Atom, Vec<Dom<Element>>>> {

View file

@ -2,6 +2,9 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::borrow::Cow;
use std::cell::RefCell;
use arrayvec::ArrayVec; use arrayvec::ArrayVec;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use euclid::default::Size2D; use euclid::default::Size2D;
@ -9,20 +12,25 @@ use ipc_channel::ipc;
use script_layout_interface::HTMLCanvasDataSource; use script_layout_interface::HTMLCanvasDataSource;
use webgpu::swapchain::WebGPUContextId; use webgpu::swapchain::WebGPUContextId;
use webgpu::wgc::id; use webgpu::wgc::id;
use webgpu::{WebGPU, WebGPURequest, WebGPUTexture, PRESENTATION_BUFFER_COUNT}; use webgpu::{
use webrender_api::{units, ImageFormat, ImageKey}; ContextConfiguration, WebGPU, WebGPURequest, WebGPUTexture, PRESENTATION_BUFFER_COUNT,
};
use webrender_api::units::DeviceIntSize;
use webrender_api::ImageKey;
use super::bindings::codegen::Bindings::WebGPUBinding::GPUTextureUsageConstants; use super::bindings::codegen::Bindings::WebGPUBinding::{
GPUCanvasAlphaMode, GPUTextureUsageConstants,
};
use super::bindings::codegen::UnionTypes::HTMLCanvasElementOrOffscreenCanvas; use super::bindings::codegen::UnionTypes::HTMLCanvasElementOrOffscreenCanvas;
use super::bindings::error::{Error, Fallible}; use super::bindings::error::{Error, Fallible};
use super::bindings::root::MutNullableDom; use super::bindings::root::MutNullableDom;
use super::bindings::str::USVString; use super::bindings::str::USVString;
use super::gpuconvert::convert_texture_descriptor;
use super::gputexture::GPUTexture; use super::gputexture::GPUTexture;
use crate::dom::bindings::codegen::Bindings::HTMLCanvasElementBinding::HTMLCanvasElement_Binding::HTMLCanvasElementMethods; use crate::dom::bindings::codegen::Bindings::WebGPUBinding::GPUTexture_Binding::GPUTextureMethods;
use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{ use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
GPUCanvasConfiguration, GPUCanvasContextMethods, GPUDeviceMethods, GPUExtent3D, GPUCanvasConfiguration, GPUCanvasContextMethods, GPUDeviceMethods, GPUExtent3D,
GPUExtent3DDict, GPUObjectDescriptorBase, GPUTextureDescriptor, GPUTextureDimension, GPUExtent3DDict, GPUObjectDescriptorBase, GPUTextureDescriptor, GPUTextureDimension,
GPUTextureFormat,
}; };
use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
@ -85,6 +93,29 @@ impl malloc_size_of::MallocSizeOf for HTMLCanvasElementOrOffscreenCanvas {
} }
} }
impl HTMLCanvasElementOrOffscreenCanvas {
fn size(&self) -> Size2D<u64> {
match self {
HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) => {
canvas.get_size().cast()
},
HTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(canvas) => canvas.get_size(),
}
}
}
#[derive(Clone, Debug, Default, JSTraceable, MallocSizeOf)]
/// Helps observe changes on swapchain
struct DrawingBuffer {
#[no_trace]
size: DeviceIntSize,
/// image is transparent black
cleared: bool,
#[ignore_malloc_size_of = "Defined in wgpu"]
#[no_trace]
config: Option<ContextConfiguration>,
}
#[dom_struct] #[dom_struct]
pub struct GPUCanvasContext { pub struct GPUCanvasContext {
reflector_: Reflector, reflector_: Reflector,
@ -99,14 +130,34 @@ pub struct GPUCanvasContext {
webrender_image: ImageKey, webrender_image: ImageKey,
#[no_trace] #[no_trace]
context_id: WebGPUContextId, context_id: WebGPUContextId,
#[ignore_malloc_size_of = "manual writing is hard"]
/// <https://gpuweb.github.io/gpuweb/#dom-gpucanvascontext-configuration-slot>
configuration: RefCell<Option<GPUCanvasConfiguration>>,
/// <https://gpuweb.github.io/gpuweb/#dom-gpucanvascontext-texturedescriptor-slot>
texture_descriptor: RefCell<Option<GPUTextureDescriptor>>,
/// Conceptually <https://gpuweb.github.io/gpuweb/#dom-gpucanvascontext-drawingbuffer-slot>
drawing_buffer: RefCell<DrawingBuffer>,
/// <https://gpuweb.github.io/gpuweb/#dom-gpucanvascontext-currenttexture-slot> /// <https://gpuweb.github.io/gpuweb/#dom-gpucanvascontext-currenttexture-slot>
texture: MutNullableDom<GPUTexture>, current_texture: MutNullableDom<GPUTexture>,
} }
impl GPUCanvasContext { impl GPUCanvasContext {
fn new_inherited(canvas: HTMLCanvasElementOrOffscreenCanvas, channel: WebGPU) -> Self { fn new_inherited(
global: &GlobalScope,
canvas: HTMLCanvasElementOrOffscreenCanvas,
channel: WebGPU,
) -> Self {
let (sender, receiver) = ipc::channel().unwrap(); let (sender, receiver) = ipc::channel().unwrap();
if let Err(e) = channel.0.send(WebGPURequest::CreateContext(sender)) { let size = canvas.size().cast().cast_unit();
let mut buffer_ids = ArrayVec::<id::BufferId, PRESENTATION_BUFFER_COUNT>::new();
for _ in 0..PRESENTATION_BUFFER_COUNT {
buffer_ids.push(global.wgpu_id_hub().create_buffer_id());
}
if let Err(e) = channel.0.send(WebGPURequest::CreateContext {
buffer_ids,
size,
sender,
}) {
warn!("Failed to send CreateContext ({:?})", e); warn!("Failed to send CreateContext ({:?})", e);
} }
let (external_id, webrender_image) = receiver.recv().unwrap(); let (external_id, webrender_image) = receiver.recv().unwrap();
@ -116,13 +167,21 @@ impl GPUCanvasContext {
canvas, canvas,
webrender_image, webrender_image,
context_id: WebGPUContextId(external_id.0), context_id: WebGPUContextId(external_id.0),
texture: MutNullableDom::default(), drawing_buffer: RefCell::new(DrawingBuffer {
size,
cleared: true,
..Default::default()
}),
configuration: RefCell::new(None),
texture_descriptor: RefCell::new(None),
current_texture: MutNullableDom::default(),
} }
} }
pub fn new(global: &GlobalScope, canvas: &HTMLCanvasElement, channel: WebGPU) -> DomRoot<Self> { pub fn new(global: &GlobalScope, canvas: &HTMLCanvasElement, channel: WebGPU) -> DomRoot<Self> {
reflect_dom_object( reflect_dom_object(
Box::new(GPUCanvasContext::new_inherited( Box::new(GPUCanvasContext::new_inherited(
global,
HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(DomRoot::from_ref(canvas)), HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(DomRoot::from_ref(canvas)),
channel, channel,
)), )),
@ -131,17 +190,93 @@ impl GPUCanvasContext {
} }
} }
// Abstract ops from spec
impl GPUCanvasContext { impl GPUCanvasContext {
fn layout_handle(&self) -> HTMLCanvasDataSource { /// <https://gpuweb.github.io/gpuweb/#abstract-opdef-gputexturedescriptor-for-the-canvas-and-configuration>
HTMLCanvasDataSource::WebGPU(self.webrender_image) fn texture_descriptor_for_canvas(
&self,
configuration: &GPUCanvasConfiguration,
) -> GPUTextureDescriptor {
let size = self.size();
GPUTextureDescriptor {
format: configuration.format,
// We need to add `COPY_SRC` so we can copy texture to presentation buffer
// causes FAIL on webgpu:web_platform,canvas,configure:usage:*
usage: configuration.usage | GPUTextureUsageConstants::COPY_SRC,
size: GPUExtent3D::GPUExtent3DDict(GPUExtent3DDict {
width: size.width as u32,
height: size.height as u32,
depthOrArrayLayers: 1,
}),
viewFormats: configuration.viewFormats.clone(),
// other members to default
mipLevelCount: 1,
sampleCount: 1,
parent: GPUObjectDescriptorBase {
label: USVString::default(),
},
dimension: GPUTextureDimension::_2d,
}
} }
pub fn send_swap_chain_present(&self) { /// <https://gpuweb.github.io/gpuweb/#abstract-opdef-expire-the-current-texture>
let texture_id = self.texture_id().unwrap().0; fn expire_current_texture(&self) {
if let Some(current_texture) = self.current_texture.take() {
// Make copy of texture content
self.send_swap_chain_present(current_texture.id());
// Step 1
current_texture.Destroy()
}
}
/// <https://gpuweb.github.io/gpuweb/#abstract-opdef-replace-the-drawing-buffer>
fn replace_drawing_buffer(&self) {
// Step 1
self.expire_current_texture();
// Step 2
let configuration = self.configuration.borrow();
// Step 3
let mut drawing_buffer = self.drawing_buffer.borrow_mut();
drawing_buffer.size = self.size().cast().cast_unit();
drawing_buffer.cleared = true;
if let Some(configuration) = configuration.as_ref() {
drawing_buffer.config = Some(ContextConfiguration {
device_id: configuration.device.id().0,
queue_id: configuration.device.queue_id().0,
format: configuration.format.into(),
is_opaque: matches!(configuration.alphaMode, GPUCanvasAlphaMode::Opaque),
});
} else {
drawing_buffer.config.take();
};
// TODO: send less
self.channel
.0
.send(WebGPURequest::UpdateContext {
context_id: self.context_id,
size: drawing_buffer.size,
configuration: drawing_buffer.config.clone(),
})
.expect("Failed to update webgpu context");
}
}
// Internal helper methods
impl GPUCanvasContext {
fn layout_handle(&self) -> HTMLCanvasDataSource {
if self.drawing_buffer.borrow().cleared {
HTMLCanvasDataSource::Empty
} else {
HTMLCanvasDataSource::WebGPU(self.webrender_image)
}
}
fn send_swap_chain_present(&self, texture_id: WebGPUTexture) {
self.drawing_buffer.borrow_mut().cleared = false;
let encoder_id = self.global().wgpu_id_hub().create_command_encoder_id(); let encoder_id = self.global().wgpu_id_hub().create_command_encoder_id();
if let Err(e) = self.channel.0.send(WebGPURequest::SwapChainPresent { if let Err(e) = self.channel.0.send(WebGPURequest::SwapChainPresent {
context_id: self.context_id, context_id: self.context_id,
texture_id, texture_id: texture_id.0,
encoder_id, encoder_id,
}) { }) {
warn!( warn!(
@ -151,29 +286,42 @@ impl GPUCanvasContext {
} }
} }
pub fn context_id(&self) -> WebGPUContextId { fn size(&self) -> Size2D<u64> {
self.canvas.size()
}
}
// public methods for canvas handling
// these methods should probably be behind trait for all canvases
impl GPUCanvasContext {
pub(crate) fn context_id(&self) -> WebGPUContextId {
self.context_id self.context_id
} }
pub fn texture_id(&self) -> Option<WebGPUTexture> { pub(crate) fn mark_as_dirty(&self) {
self.texture.get().map(|t| t.id())
}
pub fn mark_as_dirty(&self) {
if let HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) = &self.canvas { if let HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) = &self.canvas {
canvas.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage); canvas.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
let document = document_from_node(&**canvas); let document = document_from_node(&**canvas);
document.add_dirty_webgpu_canvas(self); document.add_dirty_webgpu_canvas(self);
} }
// TODO(sagudev): offscreen canvas also dirty?
} }
fn size(&self) -> Size2D<u64> { /// <https://gpuweb.github.io/gpuweb/#abstract-opdef-updating-the-rendering-of-a-webgpu-canvas>
match &self.canvas { pub(crate) fn update_rendering_of_webgpu_canvas(&self) {
HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) => { // Step 1
Size2D::new(canvas.Width() as u64, canvas.Height() as u64) self.expire_current_texture();
}, }
HTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(canvas) => canvas.get_size(),
/// <https://gpuweb.github.io/gpuweb/#abstract-opdef-update-the-canvas-size>
pub(crate) fn resize(&self) {
// Step 1
self.replace_drawing_buffer();
// Step 2
let configuration = self.configuration.borrow();
// Step 3
if let Some(configuration) = configuration.as_ref() {
self.texture_descriptor
.replace(Some(self.texture_descriptor_for_canvas(configuration)));
} }
} }
} }
@ -192,110 +340,84 @@ impl GPUCanvasContextMethods for GPUCanvasContext {
/// <https://gpuweb.github.io/gpuweb/#dom-gpucanvascontext-configure> /// <https://gpuweb.github.io/gpuweb/#dom-gpucanvascontext-configure>
fn Configure(&self, configuration: &GPUCanvasConfiguration) -> Fallible<()> { fn Configure(&self, configuration: &GPUCanvasConfiguration) -> Fallible<()> {
// Step 1 is let
// Step 2
configuration
.device
.validate_texture_format_required_features(&configuration.format)?;
// Mapping between wr and wgpu can be determined by inspecting
// https://github.com/gfx-rs/wgpu/blob/9b36a3e129da04b018257564d5129caff240cb75/wgpu-hal/src/gles/conv.rs#L10
// https://github.com/servo/webrender/blob/1e73f384a7a86e413f91e9e430436a9bd83381cd/webrender/src/device/gl.rs#L4064
let format = match configuration.format {
GPUTextureFormat::R8unorm => ImageFormat::R8,
GPUTextureFormat::Bgra8unorm | GPUTextureFormat::Bgra8unorm_srgb => ImageFormat::BGRA8,
GPUTextureFormat::Rgba8unorm | GPUTextureFormat::Rgba8unorm_srgb => ImageFormat::RGBA8,
GPUTextureFormat::Rgba32float => ImageFormat::RGBAF32,
GPUTextureFormat::Rgba32sint => ImageFormat::RGBAI32,
GPUTextureFormat::Rg8unorm => ImageFormat::RG8,
_ => {
return Err(Error::Type(format!(
"SwapChain format({:?}) not supported",
configuration.format
)))
},
};
// Step 3
for view_format in &configuration.viewFormats {
configuration
.device
.validate_texture_format_required_features(view_format)?;
}
// Step 4 // Step 4
let size = self.size(); let descriptor = self.texture_descriptor_for_canvas(configuration);
let text_desc = GPUTextureDescriptor {
format: configuration.format,
mipLevelCount: 1,
sampleCount: 1,
// We need to add `COPY_SRC` so we can copy texture to presentation buffer
usage: configuration.usage | GPUTextureUsageConstants::COPY_SRC,
size: GPUExtent3D::GPUExtent3DDict(GPUExtent3DDict {
width: size.width as u32,
height: size.height as u32,
depthOrArrayLayers: 1,
}),
viewFormats: configuration.viewFormats.clone(),
// other members to default
parent: GPUObjectDescriptorBase {
label: USVString::default(),
},
dimension: GPUTextureDimension::_2d,
};
// Step 8 // Step 2&3
let mut buffer_ids = ArrayVec::<id::BufferId, PRESENTATION_BUFFER_COUNT>::new(); let (mut desc, _) = convert_texture_descriptor(&descriptor, &configuration.device)?;
for _ in 0..PRESENTATION_BUFFER_COUNT { desc.label = Some(Cow::Borrowed(
buffer_ids.push(self.global().wgpu_id_hub().create_buffer_id()); "dummy texture for texture descriptor validation",
} ));
// Step 5
self.configuration.replace(Some(configuration.clone()));
// Step 6
self.texture_descriptor.replace(Some(descriptor));
// Step 7
self.replace_drawing_buffer();
// Step 8: Validate texture descriptor
let texture_id = self.global().wgpu_id_hub().create_texture_id();
self.channel self.channel
.0 .0
.send(WebGPURequest::CreateSwapChain { .send(WebGPURequest::ValidateTextureDescriptor {
device_id: configuration.device.id().0, device_id: configuration.device.id().0,
queue_id: configuration.device.GetQueue().id().0, texture_id,
buffer_ids, descriptor: desc,
context_id: self.context_id,
image_key: self.webrender_image,
format,
size: units::DeviceIntSize::new(size.width as i32, size.height as i32),
}) })
.expect("Failed to create WebGPU SwapChain"); .expect("Failed to create WebGPU SwapChain");
self.texture.set(Some(
&configuration.device.CreateTexture(&text_desc).unwrap(),
));
Ok(()) Ok(())
} }
/// <https://gpuweb.github.io/gpuweb/#dom-gpucanvascontext-unconfigure> /// <https://gpuweb.github.io/gpuweb/#dom-gpucanvascontext-unconfigure>
fn Unconfigure(&self) { fn Unconfigure(&self) {
if let Some(texture) = self.texture.take() { // Step 1
if let Err(e) = self.channel.0.send(WebGPURequest::DestroySwapChain { self.configuration.take();
context_id: self.context_id, // Step 2
image_key: self.webrender_image, self.current_texture.take();
}) { // Step 3
warn!( self.replace_drawing_buffer();
"Failed to send DestroySwapChain-ImageKey({:?}) ({})",
self.webrender_image, e
);
}
drop(texture);
}
} }
/// <https://gpuweb.github.io/gpuweb/#dom-gpucanvascontext-getcurrenttexture> /// <https://gpuweb.github.io/gpuweb/#dom-gpucanvascontext-getcurrenttexture>
fn GetCurrentTexture(&self) -> Fallible<DomRoot<GPUTexture>> { fn GetCurrentTexture(&self) -> Fallible<DomRoot<GPUTexture>> {
// Step 5. // Step 1
let configuration = self.configuration.borrow();
let Some(configuration) = configuration.as_ref() else {
return Err(Error::InvalidState);
};
// Step 2
let texture_descriptor = self.texture_descriptor.borrow();
let texture_descriptor = texture_descriptor.as_ref().unwrap();
// Step 6
let current_texture = if let Some(current_texture) = self.current_texture.get() {
current_texture
} else {
// Step 3&4
self.replace_drawing_buffer();
let current_texture = configuration.device.CreateTexture(&texture_descriptor)?;
self.current_texture.set(Some(&current_texture));
current_texture
};
// Step 5
self.mark_as_dirty(); self.mark_as_dirty();
// Step 6. // Step 6
self.texture.get().ok_or(Error::InvalidState) Ok(current_texture)
} }
} }
impl Drop for GPUCanvasContext { impl Drop for GPUCanvasContext {
fn drop(&mut self) { fn drop(&mut self) {
self.Unconfigure() if let Err(e) = self.channel.0.send(WebGPURequest::DestroyContext {
context_id: self.context_id,
}) {
warn!(
"Failed to send DestroySwapChain-ImageKey({:?}) ({})",
self.webrender_image, e
);
}
} }
} }

View file

@ -8,10 +8,11 @@ use std::num::NonZeroU64;
use webgpu::wgc::binding_model::{BindGroupEntry, BindingResource, BufferBinding}; use webgpu::wgc::binding_model::{BindGroupEntry, BindingResource, BufferBinding};
use webgpu::wgc::command as wgpu_com; use webgpu::wgc::command as wgpu_com;
use webgpu::wgc::pipeline::ProgrammableStageDescriptor; use webgpu::wgc::pipeline::ProgrammableStageDescriptor;
use webgpu::wgc::resource::TextureDescriptor;
use webgpu::wgt::{self, AstcBlock, AstcChannel}; use webgpu::wgt::{self, AstcBlock, AstcChannel};
use super::bindings::codegen::Bindings::WebGPUBinding::{ use super::bindings::codegen::Bindings::WebGPUBinding::{
GPUProgrammableStage, GPUTextureDimension, GPUProgrammableStage, GPUTextureDescriptor, GPUTextureDimension,
}; };
use super::bindings::error::Error; use super::bindings::error::Error;
use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{ use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
@ -506,6 +507,7 @@ impl<'a> From<&GPUObjectDescriptorBase> for Option<Cow<'a, str>> {
} }
} }
} }
pub fn convert_bind_group_layout_entry( pub fn convert_bind_group_layout_entry(
bgle: &GPUBindGroupLayoutEntry, bgle: &GPUBindGroupLayoutEntry,
device: &GPUDevice, device: &GPUDevice,
@ -581,6 +583,28 @@ pub fn convert_bind_group_layout_entry(
})) }))
} }
pub fn convert_texture_descriptor(
descriptor: &GPUTextureDescriptor,
device: &GPUDevice,
) -> Fallible<(TextureDescriptor<'static>, wgt::Extent3d)> {
let size = (&descriptor.size).try_into()?;
let desc = TextureDescriptor {
label: (&descriptor.parent).into(),
size,
mip_level_count: descriptor.mipLevelCount,
sample_count: descriptor.sampleCount,
dimension: descriptor.dimension.into(),
format: device.validate_texture_format_required_features(&descriptor.format)?,
usage: wgt::TextureUsages::from_bits_retain(descriptor.usage),
view_formats: descriptor
.viewFormats
.iter()
.map(|tf| device.validate_texture_format_required_features(tf))
.collect::<Fallible<_>>()?,
};
Ok((desc, size))
}
impl TryFrom<&GPUColor> for wgt::Color { impl TryFrom<&GPUColor> for wgt::Color {
type Error = Error; type Error = Error;

View file

@ -153,6 +153,10 @@ impl GPUDevice {
self.device self.device
} }
pub fn queue_id(&self) -> webgpu::WebGPUQueue {
self.default_queue.id()
}
pub fn channel(&self) -> WebGPU { pub fn channel(&self) -> WebGPU {
self.channel.clone() self.channel.clone()
} }

View file

@ -9,6 +9,7 @@ use webgpu::wgc::resource;
use webgpu::{wgt, WebGPU, WebGPURequest, WebGPUTexture, WebGPUTextureView}; use webgpu::{wgt, WebGPU, WebGPURequest, WebGPUTexture, WebGPUTextureView};
use super::bindings::error::Fallible; use super::bindings::error::Fallible;
use super::gpuconvert::convert_texture_descriptor;
use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{ use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
GPUTextureAspect, GPUTextureDescriptor, GPUTextureDimension, GPUTextureFormat, GPUTextureAspect, GPUTextureDescriptor, GPUTextureDimension, GPUTextureFormat,
@ -127,21 +128,7 @@ impl GPUTexture {
device: &GPUDevice, device: &GPUDevice,
descriptor: &GPUTextureDescriptor, descriptor: &GPUTextureDescriptor,
) -> Fallible<DomRoot<GPUTexture>> { ) -> Fallible<DomRoot<GPUTexture>> {
let size = (&descriptor.size).try_into()?; let (desc, size) = convert_texture_descriptor(descriptor, device)?;
let desc = wgt::TextureDescriptor {
label: (&descriptor.parent).into(),
size,
mip_level_count: descriptor.mipLevelCount,
sample_count: descriptor.sampleCount,
dimension: descriptor.dimension.into(),
format: device.validate_texture_format_required_features(&descriptor.format)?,
usage: wgt::TextureUsages::from_bits_retain(descriptor.usage),
view_formats: descriptor
.viewFormats
.iter()
.map(|tf| device.validate_texture_format_required_features(tf))
.collect::<Fallible<_>>()?,
};
let texture_id = device.global().wgpu_id_hub().create_texture_id(); let texture_id = device.global().wgpu_id_hub().create_texture_id();

View file

@ -105,7 +105,7 @@ impl HTMLCanvasElement {
}, },
CanvasContext::WebGL(ref context) => context.recreate(size), CanvasContext::WebGL(ref context) => context.recreate(size),
CanvasContext::WebGL2(ref context) => context.recreate(size), CanvasContext::WebGL2(ref context) => context.recreate(size),
CanvasContext::WebGPU(_) => unimplemented!(), CanvasContext::WebGPU(ref context) => context.resize(),
} }
} }
} }

View file

@ -10,7 +10,7 @@ use base::id::PipelineId;
use ipc_channel::ipc::{IpcSender, IpcSharedMemory}; use ipc_channel::ipc::{IpcSender, IpcSharedMemory};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use webrender_api::units::DeviceIntSize; use webrender_api::units::DeviceIntSize;
use webrender_api::{ImageFormat, ImageKey}; use webrender_api::ImageKey;
use wgc::binding_model::{ use wgc::binding_model::{
BindGroupDescriptor, BindGroupLayoutDescriptor, PipelineLayoutDescriptor, BindGroupDescriptor, BindGroupLayoutDescriptor, PipelineLayoutDescriptor,
}; };
@ -34,6 +34,14 @@ use crate::render_commands::RenderCommand;
use crate::swapchain::WebGPUContextId; use crate::swapchain::WebGPUContextId;
use crate::{Error, ErrorFilter, WebGPUResponse, PRESENTATION_BUFFER_COUNT}; use crate::{Error, ErrorFilter, WebGPUResponse, PRESENTATION_BUFFER_COUNT};
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub struct ContextConfiguration {
pub device_id: id::DeviceId,
pub queue_id: id::QueueId,
pub format: wgt::TextureFormat,
pub is_opaque: bool,
}
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
pub enum WebGPURequest { pub enum WebGPURequest {
BufferMapAsync { BufferMapAsync {
@ -103,7 +111,6 @@ pub enum WebGPURequest {
/// present only on ASYNC versions /// present only on ASYNC versions
async_sender: Option<IpcSender<WebGPUResponse>>, async_sender: Option<IpcSender<WebGPUResponse>>,
}, },
CreateContext(IpcSender<(WebGPUContextId, ImageKey)>),
CreatePipelineLayout { CreatePipelineLayout {
device_id: id::DeviceId, device_id: id::DeviceId,
pipeline_layout_id: id::PipelineLayoutId, pipeline_layout_id: id::PipelineLayoutId,
@ -129,14 +136,31 @@ pub enum WebGPURequest {
label: Option<String>, label: Option<String>,
sender: IpcSender<WebGPUResponse>, sender: IpcSender<WebGPUResponse>,
}, },
CreateSwapChain { /// Creates context
device_id: id::DeviceId, CreateContext {
queue_id: id::QueueId,
buffer_ids: ArrayVec<id::BufferId, PRESENTATION_BUFFER_COUNT>, buffer_ids: ArrayVec<id::BufferId, PRESENTATION_BUFFER_COUNT>,
context_id: WebGPUContextId,
image_key: ImageKey,
format: ImageFormat,
size: DeviceIntSize, size: DeviceIntSize,
sender: IpcSender<(WebGPUContextId, ImageKey)>,
},
/// Recreates swapchain (if needed)
UpdateContext {
context_id: WebGPUContextId,
size: DeviceIntSize,
configuration: Option<ContextConfiguration>,
},
/// Reads texture to swapchains buffer and maps it
SwapChainPresent {
context_id: WebGPUContextId,
texture_id: id::TextureId,
encoder_id: id::CommandEncoderId,
},
ValidateTextureDescriptor {
device_id: id::DeviceId,
texture_id: id::TextureId,
descriptor: TextureDescriptor<'static>,
},
DestroyContext {
context_id: WebGPUContextId,
}, },
CreateTexture { CreateTexture {
device_id: id::DeviceId, device_id: id::DeviceId,
@ -152,10 +176,6 @@ pub enum WebGPURequest {
DestroyBuffer(id::BufferId), DestroyBuffer(id::BufferId),
DestroyDevice(id::DeviceId), DestroyDevice(id::DeviceId),
DestroyTexture(id::TextureId), DestroyTexture(id::TextureId),
DestroySwapChain {
context_id: WebGPUContextId,
image_key: ImageKey,
},
DropTexture(id::TextureId), DropTexture(id::TextureId),
DropAdapter(id::AdapterId), DropAdapter(id::AdapterId),
DropDevice(id::DeviceId), DropDevice(id::DeviceId),
@ -254,11 +274,6 @@ pub enum WebGPURequest {
queue_id: id::QueueId, queue_id: id::QueueId,
command_buffers: Vec<id::CommandBufferId>, command_buffers: Vec<id::CommandBufferId>,
}, },
SwapChainPresent {
context_id: WebGPUContextId,
texture_id: id::TextureId,
encoder_id: id::CommandEncoderId,
},
UnmapBuffer { UnmapBuffer {
buffer_id: id::BufferId, buffer_id: id::BufferId,
array_buffer: IpcSharedMemory, array_buffer: IpcSharedMemory,

View file

@ -4,7 +4,7 @@
use log::warn; use log::warn;
use swapchain::WGPUImageMap; use swapchain::WGPUImageMap;
pub use swapchain::{PresentationData, WGPUExternalImages}; pub use swapchain::{ContextData, WGPUExternalImages};
use webrender::RenderApiSender; use webrender::RenderApiSender;
use wgpu_thread::WGPU; use wgpu_thread::WGPU;
pub use {wgpu_core as wgc, wgpu_types as wgt}; pub use {wgpu_core as wgc, wgpu_types as wgt};

View file

@ -3,21 +3,21 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::collections::HashMap; use std::collections::HashMap;
use std::ops::ControlFlow;
use std::ptr::NonNull; use std::ptr::NonNull;
use std::slice; use std::slice;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use arrayvec::ArrayVec; use arrayvec::ArrayVec;
use euclid::default::Size2D; use euclid::default::Size2D;
use ipc_channel::ipc::IpcSender;
use log::{error, warn}; use log::{error, warn};
use malloc_size_of::MallocSizeOf; use malloc_size_of::MallocSizeOf;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use webrender::{RenderApi, Transaction}; use webrender::{RenderApi, Transaction};
use webrender_api::units::DeviceIntSize; use webrender_api::units::DeviceIntSize;
use webrender_api::{ use webrender_api::{
DirtyRect, ExternalImageData, ExternalImageId, ExternalImageType, ImageData, ImageDescriptor, DirtyRect, DocumentId, ExternalImageData, ExternalImageId, ExternalImageType, ImageData,
ImageDescriptorFlags, ImageFormat, ImageKey, ImageDescriptor, ImageDescriptorFlags, ImageFormat, ImageKey,
}; };
use webrender_traits::{WebrenderExternalImageApi, WebrenderImageSource}; use webrender_traits::{WebrenderExternalImageApi, WebrenderImageSource};
use wgpu_core::device::HostMap; use wgpu_core::device::HostMap;
@ -25,9 +25,10 @@ use wgpu_core::global::Global;
use wgpu_core::id; use wgpu_core::id;
use wgpu_core::resource::{BufferAccessError, BufferMapCallback, BufferMapOperation}; use wgpu_core::resource::{BufferAccessError, BufferMapCallback, BufferMapOperation};
use crate::{wgt, WebGPUMsg}; use crate::{wgt, ContextConfiguration, Error, WebGPUMsg};
pub const PRESENTATION_BUFFER_COUNT: usize = 10; pub const PRESENTATION_BUFFER_COUNT: usize = 10;
const DEFAULT_IMAGE_FORMAT: ImageFormat = ImageFormat::RGBA8;
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
pub struct WebGPUContextId(pub u64); pub struct WebGPUContextId(pub u64);
@ -38,7 +39,7 @@ impl MallocSizeOf for WebGPUContextId {
} }
} }
pub type WGPUImageMap = Arc<Mutex<HashMap<WebGPUContextId, PresentationData>>>; pub type WGPUImageMap = Arc<Mutex<HashMap<WebGPUContextId, ContextData>>>;
struct GPUPresentationBuffer { struct GPUPresentationBuffer {
global: Arc<Global>, global: Arc<Global>,
@ -84,20 +85,20 @@ pub struct WGPUExternalImages {
impl WebrenderExternalImageApi for WGPUExternalImages { impl WebrenderExternalImageApi for WGPUExternalImages {
fn lock(&mut self, id: u64) -> (WebrenderImageSource, Size2D<i32>) { fn lock(&mut self, id: u64) -> (WebrenderImageSource, Size2D<i32>) {
let id = WebGPUContextId(id); let id = WebGPUContextId(id);
let size; let webgpu_contexts = self.images.lock().unwrap();
let data; let context_data = webgpu_contexts.get(&id).unwrap();
if let Some(present_data) = self.images.lock().unwrap().get(&id) { let size = context_data.image_desc.size().cast_unit();
size = present_data.image_desc.size.cast_unit(); let data = if let Some(present_buffer) = context_data
data = if let Some(present_data) = &present_data.data { .swap_chain
present_data.slice().to_vec() .as_ref()
} else { .map(|swap_chain| swap_chain.data.as_ref())
present_data.dummy_data() .flatten()
}; {
present_buffer.slice().to_vec()
} else { } else {
size = Size2D::new(0, 0); context_data.dummy_data()
data = Vec::new(); };
} self.locked_ids.insert(id, data);
let _ = self.locked_ids.insert(id, data);
( (
WebrenderImageSource::Raw(self.locked_ids.get(&id).unwrap().as_slice()), WebrenderImageSource::Raw(self.locked_ids.get(&id).unwrap().as_slice()),
size, size,
@ -106,13 +107,13 @@ impl WebrenderExternalImageApi for WGPUExternalImages {
fn unlock(&mut self, id: u64) { fn unlock(&mut self, id: u64) {
let id = WebGPUContextId(id); let id = WebGPUContextId(id);
let _ = self.locked_ids.remove(&id); self.locked_ids.remove(&id);
} }
} }
/// States of presentation buffer /// States of presentation buffer
#[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd)] #[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
pub enum PresentationBufferState { enum PresentationBufferState {
/// Initial state, buffer has yet to be created, /// Initial state, buffer has yet to be created,
/// only its id is reserved /// only its id is reserved
#[default] #[default]
@ -125,61 +126,105 @@ pub enum PresentationBufferState {
Mapped, Mapped,
} }
pub struct PresentationData { struct SwapChain {
device_id: id::DeviceId, device_id: id::DeviceId,
queue_id: id::QueueId, queue_id: id::QueueId,
data: Option<GPUPresentationBuffer>, data: Option<GPUPresentationBuffer>,
buffer_ids: ArrayVec<(id::BufferId, PresentationBufferState), PRESENTATION_BUFFER_COUNT>,
image_key: ImageKey,
image_desc: ImageDescriptor,
image_data: ImageData,
} }
impl PresentationData { #[derive(Clone, Copy, Debug, PartialEq)]
pub fn new( pub struct WebGPUImageDescriptor(pub ImageDescriptor);
device_id: id::DeviceId,
queue_id: id::QueueId, impl WebGPUImageDescriptor {
buffer_ids: ArrayVec<id::BufferId, PRESENTATION_BUFFER_COUNT>, fn new(format: ImageFormat, size: DeviceIntSize, is_opaque: bool) -> Self {
let stride = (((size.width * format.bytes_per_pixel()) |
(wgt::COPY_BYTES_PER_ROW_ALIGNMENT as i32 - 1)) +
1) as i32;
Self(ImageDescriptor {
format,
size,
stride: Some(stride),
offset: 0,
flags: if is_opaque {
ImageDescriptorFlags::IS_OPAQUE
} else {
ImageDescriptorFlags::empty()
},
})
}
fn default(size: DeviceIntSize) -> Self {
Self::new(DEFAULT_IMAGE_FORMAT, size, false)
}
/// Returns true if needs image update (if it's changed)
fn update(&mut self, new: Self) -> bool {
if self.0 != new.0 {
self.0 = new.0;
true
} else {
false
}
}
fn buffer_stride(&self) -> i32 {
self.0
.stride
.expect("Stride should be set by WebGPUImageDescriptor")
}
fn buffer_size(&self) -> wgt::BufferAddress {
(self.buffer_stride() * self.0.size.height) as wgt::BufferAddress
}
fn size(&self) -> DeviceIntSize {
self.0.size
}
}
pub struct ContextData {
image_key: ImageKey,
image_desc: WebGPUImageDescriptor,
image_data: ImageData,
buffer_ids: ArrayVec<(id::BufferId, PresentationBufferState), PRESENTATION_BUFFER_COUNT>,
/// If there is no associated swapchain the context is dummy (transparent black)
swap_chain: Option<SwapChain>,
}
impl ContextData {
/// Init ContextData as dummy (transparent black)
fn new(
context_id: WebGPUContextId,
image_key: ImageKey, image_key: ImageKey,
image_desc: ImageDescriptor, size: DeviceIntSize,
image_data: ImageData, buffer_ids: ArrayVec<id::BufferId, PRESENTATION_BUFFER_COUNT>,
) -> Self { ) -> Self {
let image_data = ImageData::External(ExternalImageData {
id: ExternalImageId(context_id.0),
channel_index: 0,
image_type: ExternalImageType::Buffer,
});
Self { Self {
device_id, image_key,
queue_id, image_desc: WebGPUImageDescriptor::default(size),
data: None, image_data,
swap_chain: None,
buffer_ids: buffer_ids buffer_ids: buffer_ids
.iter() .iter()
.map(|&id| (id, PresentationBufferState::Unassigned)) .map(|&buffer_id| (buffer_id, PresentationBufferState::Unassigned))
.collect(), .collect(),
image_key,
image_desc,
image_data,
} }
} }
fn dummy_data(&self) -> Vec<u8> { fn dummy_data(&self) -> Vec<u8> {
let size = (self vec![0; self.image_desc.buffer_size() as usize]
.image_desc
.stride
.expect("Stride should be set when creating swapchain") *
self.image_desc.size.height) as usize;
vec![0; size]
}
fn buffer_stride(&self) -> i32 {
self.image_desc
.stride
.expect("Stride should be set when creating swapchain")
}
fn buffer_size(&self) -> wgt::BufferAddress {
(self.buffer_stride() * self.image_desc.size.height) as wgt::BufferAddress
} }
/// Returns id of available buffer /// Returns id of available buffer
/// and sets state to PresentationBufferState::Mapping /// and sets state to PresentationBufferState::Mapping
fn get_available_buffer(&'_ mut self, global: &Arc<Global>) -> Option<id::BufferId> { fn get_available_buffer(&'_ mut self, global: &Arc<Global>) -> Option<id::BufferId> {
assert!(self.swap_chain.is_some());
if let Some((buffer_id, buffer_state)) = self if let Some((buffer_id, buffer_state)) = self
.buffer_ids .buffer_ids
.iter_mut() .iter_mut()
@ -196,11 +241,15 @@ impl PresentationData {
let buffer_id = *buffer_id; let buffer_id = *buffer_id;
let buffer_desc = wgt::BufferDescriptor { let buffer_desc = wgt::BufferDescriptor {
label: None, label: None,
size: self.buffer_size(), size: self.image_desc.buffer_size(),
usage: wgt::BufferUsages::MAP_READ | wgt::BufferUsages::COPY_DST, usage: wgt::BufferUsages::MAP_READ | wgt::BufferUsages::COPY_DST,
mapped_at_creation: false, mapped_at_creation: false,
}; };
let _ = global.device_create_buffer(self.device_id, &buffer_desc, Some(buffer_id)); let _ = global.device_create_buffer(
self.swap_chain.as_ref().unwrap().device_id,
&buffer_desc,
Some(buffer_id),
);
Some(buffer_id) Some(buffer_id)
} else { } else {
None None
@ -217,57 +266,135 @@ impl PresentationData {
} }
fn unmap_old_buffer(&mut self, presentation_buffer: GPUPresentationBuffer) { fn unmap_old_buffer(&mut self, presentation_buffer: GPUPresentationBuffer) {
assert!(self.swap_chain.is_some());
let buffer_state = self.get_buffer_state(presentation_buffer.buffer_id); let buffer_state = self.get_buffer_state(presentation_buffer.buffer_id);
assert_eq!(*buffer_state, PresentationBufferState::Mapped); assert_eq!(*buffer_state, PresentationBufferState::Mapped);
*buffer_state = PresentationBufferState::Available; *buffer_state = PresentationBufferState::Available;
drop(presentation_buffer); drop(presentation_buffer);
} }
fn destroy_swapchain(&mut self, global: &Arc<Global>) {
drop(self.swap_chain.take());
// free all buffers
for (buffer_id, buffer_state) in &mut self.buffer_ids {
match buffer_state {
PresentationBufferState::Unassigned => {
/* These buffer were not yet created in wgpu */
},
_ => {
global.buffer_drop(*buffer_id);
},
}
*buffer_state = PresentationBufferState::Unassigned;
}
}
fn destroy(
mut self,
global: &Arc<Global>,
script_sender: &IpcSender<WebGPUMsg>,
webrender_api: &Arc<Mutex<RenderApi>>,
webrender_document: DocumentId,
) {
self.destroy_swapchain(global);
for (buffer_id, _) in self.buffer_ids {
if let Err(e) = script_sender.send(WebGPUMsg::FreeBuffer(buffer_id)) {
warn!("Unable to send FreeBuffer({:?}) ({:?})", buffer_id, e);
};
}
let mut txn = Transaction::new();
txn.delete_image(self.image_key);
webrender_api
.lock()
.unwrap()
.send_transaction(webrender_document, txn);
}
} }
impl crate::WGPU { impl crate::WGPU {
pub(crate) fn create_swapchain( pub(crate) fn create_context(
&self, &self,
device_id: id::DeviceId,
queue_id: id::QueueId,
buffer_ids: ArrayVec<id::BufferId, PRESENTATION_BUFFER_COUNT>,
context_id: WebGPUContextId, context_id: WebGPUContextId,
format: ImageFormat,
size: DeviceIntSize,
image_key: ImageKey, image_key: ImageKey,
size: DeviceIntSize,
buffer_ids: ArrayVec<id::BufferId, PRESENTATION_BUFFER_COUNT>,
) { ) {
let image_desc = ImageDescriptor { let context_data = ContextData::new(context_id, image_key, size, buffer_ids);
format,
size,
stride: Some(
(((size.width as u32 * 4) | (wgt::COPY_BYTES_PER_ROW_ALIGNMENT - 1)) + 1) as i32,
),
offset: 0,
flags: ImageDescriptorFlags::IS_OPAQUE,
};
let image_data = ImageData::External(ExternalImageData {
id: ExternalImageId(context_id.0),
channel_index: 0,
image_type: ExternalImageType::Buffer,
});
let _ = self.wgpu_image_map.lock().unwrap().insert(
context_id,
PresentationData::new(
device_id,
queue_id,
buffer_ids,
image_key,
image_desc,
image_data.clone(),
),
);
let mut txn = Transaction::new(); let mut txn = Transaction::new();
txn.add_image(image_key, image_desc, image_data, None); txn.add_image(
image_key,
context_data.image_desc.0,
context_data.image_data.clone(),
None,
);
self.webrender_api self.webrender_api
.lock() .lock()
.unwrap() .unwrap()
.send_transaction(self.webrender_document, txn); .send_transaction(self.webrender_document, txn);
assert!(
self.wgpu_image_map
.lock()
.unwrap()
.insert(context_id, context_data)
.is_none(),
"Context should be created only once!"
);
}
pub(crate) fn update_context(
&self,
context_id: WebGPUContextId,
size: DeviceIntSize,
config: Option<ContextConfiguration>,
) {
let mut webgpu_contexts = self.wgpu_image_map.lock().unwrap();
let context_data = webgpu_contexts.get_mut(&context_id).unwrap();
// If configuration is not provided or presentation format is not valid
// the context will be dummy until recreation
let format = config
.as_ref()
.map(|config| match config.format {
wgt::TextureFormat::Rgba8Unorm => Some(ImageFormat::RGBA8),
wgt::TextureFormat::Bgra8Unorm => Some(ImageFormat::BGRA8),
_ => None,
})
.flatten();
let needs_image_update = if let Some(format) = format {
let config = config.expect("Config should exist when valid format is available");
let new_image_desc = WebGPUImageDescriptor::new(format, size, config.is_opaque);
let needs_swapchain_rebuild = context_data.swap_chain.is_none() ||
new_image_desc.buffer_size() != context_data.image_desc.buffer_size();
if needs_swapchain_rebuild {
context_data.destroy_swapchain(&self.global);
context_data.swap_chain = Some(SwapChain {
device_id: config.device_id,
queue_id: config.queue_id,
data: None,
});
}
context_data.image_desc.update(new_image_desc)
} else {
context_data.destroy_swapchain(&self.global);
context_data
.image_desc
.update(WebGPUImageDescriptor::default(size))
};
if needs_image_update {
let mut txn = Transaction::new();
txn.update_image(
context_data.image_key,
context_data.image_desc.0,
context_data.image_data.clone(),
&DirtyRect::All,
);
self.webrender_api
.lock()
.unwrap()
.send_transaction(self.webrender_document, txn);
}
} }
/// Copies data async from provided texture using encoder_id to available staging presentation buffer /// Copies data async from provided texture using encoder_id to available staging presentation buffer
@ -276,39 +403,42 @@ impl crate::WGPU {
context_id: WebGPUContextId, context_id: WebGPUContextId,
encoder_id: id::Id<id::markers::CommandEncoder>, encoder_id: id::Id<id::markers::CommandEncoder>,
texture_id: id::Id<id::markers::Texture>, texture_id: id::Id<id::markers::Texture>,
) -> ControlFlow<()> { ) -> Result<(), Box<dyn std::error::Error>> {
fn err<T: std::error::Error + 'static>(e: Option<T>) -> Result<(), T> {
if let Some(error) = e {
Err(error)
} else {
Ok(())
}
}
let global = &self.global; let global = &self.global;
let device_id; let device_id;
let queue_id; let queue_id;
let size;
let buffer_id; let buffer_id;
let buffer_stride; let image_desc;
let buffer_size;
{ {
if let Some(present_data) = self.wgpu_image_map.lock().unwrap().get_mut(&context_id) { if let Some(context_data) = self.wgpu_image_map.lock().unwrap().get_mut(&context_id) {
size = present_data.image_desc.size; let Some(swap_chain) = context_data.swap_chain.as_ref() else {
device_id = present_data.device_id; return Ok(());
queue_id = present_data.queue_id;
buffer_stride = present_data.buffer_stride();
buffer_size = present_data.buffer_size();
buffer_id = if let Some(buffer_id) = present_data.get_available_buffer(global) {
buffer_id
} else {
error!("No staging buffer available for {:?}", context_id);
return ControlFlow::Break(());
}; };
device_id = swap_chain.device_id;
queue_id = swap_chain.queue_id;
buffer_id = context_data.get_available_buffer(global).unwrap();
image_desc = context_data.image_desc;
} else { } else {
error!("Data not found for {:?}", context_id); return Ok(());
return ControlFlow::Break(());
} }
} }
let comm_desc = wgt::CommandEncoderDescriptor { label: None }; let comm_desc = wgt::CommandEncoderDescriptor { label: None };
let _ = global.device_create_command_encoder(device_id, &comm_desc, Some(encoder_id)); let (encoder_id, error) =
global.device_create_command_encoder(device_id, &comm_desc, Some(encoder_id));
err(error)?;
let buffer_cv = wgt::ImageCopyBuffer { let buffer_cv = wgt::ImageCopyBuffer {
buffer: buffer_id, buffer: buffer_id,
layout: wgt::ImageDataLayout { layout: wgt::ImageDataLayout {
offset: 0, offset: 0,
bytes_per_row: Some(buffer_stride as u32), bytes_per_row: Some(image_desc.buffer_stride() as u32),
rows_per_image: None, rows_per_image: None,
}, },
}; };
@ -319,18 +449,25 @@ impl crate::WGPU {
aspect: wgt::TextureAspect::All, aspect: wgt::TextureAspect::All,
}; };
let copy_size = wgt::Extent3d { let copy_size = wgt::Extent3d {
width: size.width as u32, width: image_desc.size().width as u32,
height: size.height as u32, height: image_desc.size().height as u32,
depth_or_array_layers: 1, depth_or_array_layers: 1,
}; };
let _ = global.command_encoder_copy_texture_to_buffer( global.command_encoder_copy_texture_to_buffer(
encoder_id, encoder_id,
&texture_cv, &texture_cv,
&buffer_cv, &buffer_cv,
&copy_size, &copy_size,
); )?;
let _ = global.command_encoder_finish(encoder_id, &wgt::CommandBufferDescriptor::default()); let (command_buffer_id, error) =
let _ = global.queue_submit(queue_id, &[encoder_id.into_command_buffer_id()]); global.command_encoder_finish(encoder_id, &wgt::CommandBufferDescriptor::default());
err(error)?;
{
let _guard = self.poller.lock();
global
.queue_submit(queue_id, &[command_buffer_id])
.map_err(Error::from_error)?;
}
let callback = { let callback = {
let global = Arc::clone(&self.global); let global = Arc::clone(&self.global);
let wgpu_image_map = Arc::clone(&self.wgpu_image_map); let wgpu_image_map = Arc::clone(&self.wgpu_image_map);
@ -343,11 +480,11 @@ impl crate::WGPU {
result, result,
global, global,
buffer_id, buffer_id,
buffer_size,
wgpu_image_map, wgpu_image_map,
context_id, context_id,
webrender_api, webrender_api,
webrender_document, webrender_document,
image_desc,
); );
})) }))
}; };
@ -355,42 +492,23 @@ impl crate::WGPU {
host: HostMap::Read, host: HostMap::Read,
callback: Some(callback), callback: Some(callback),
}; };
let _ = global.buffer_map_async(buffer_id, 0, Some(buffer_size), map_op); global.buffer_map_async(buffer_id, 0, Some(image_desc.buffer_size()), map_op)?;
self.poller.wake(); self.poller.wake();
Ok(())
ControlFlow::Continue(())
} }
pub(crate) fn destroy_swapchain( pub(crate) fn destroy_context(&mut self, context_id: WebGPUContextId) {
&mut self, self.wgpu_image_map
context_id: WebGPUContextId,
image_key: webrender_api::ImageKey,
) {
let present_data = self
.wgpu_image_map
.lock() .lock()
.unwrap() .unwrap()
.remove(&context_id) .remove(&context_id)
.unwrap();
for (buffer_id, buffer_state) in present_data.buffer_ids {
match buffer_state {
PresentationBufferState::Unassigned => {
/* These buffer were not yet created in wgpu */
},
_ => {
self.global.buffer_drop(buffer_id);
},
}
if let Err(e) = self.script_sender.send(WebGPUMsg::FreeBuffer(buffer_id)) {
warn!("Unable to send FreeBuffer({:?}) ({:?})", buffer_id, e);
};
}
let mut txn = Transaction::new();
txn.delete_image(image_key);
self.webrender_api
.lock()
.unwrap() .unwrap()
.send_transaction(self.webrender_document, txn); .destroy(
&self.global,
&self.script_sender,
&self.webrender_api,
self.webrender_document,
);
} }
} }
@ -398,26 +516,60 @@ fn update_wr_image(
result: Result<(), BufferAccessError>, result: Result<(), BufferAccessError>,
global: Arc<Global>, global: Arc<Global>,
buffer_id: id::BufferId, buffer_id: id::BufferId,
buffer_size: u64,
wgpu_image_map: WGPUImageMap, wgpu_image_map: WGPUImageMap,
context_id: WebGPUContextId, context_id: WebGPUContextId,
webrender_api: Arc<Mutex<RenderApi>>, webrender_api: Arc<Mutex<RenderApi>>,
webrender_document: webrender_api::DocumentId, webrender_document: webrender_api::DocumentId,
image_desc: WebGPUImageDescriptor,
) { ) {
match result { match result {
Ok(()) => { Ok(()) => {
if let Some(present_data) = wgpu_image_map.lock().unwrap().get_mut(&context_id) { if let Some(context_data) = wgpu_image_map.lock().unwrap().get_mut(&context_id) {
let buffer_state = present_data.get_buffer_state(buffer_id); let config_changed = image_desc != context_data.image_desc;
assert_eq!(*buffer_state, PresentationBufferState::Mapping); let buffer_state = context_data.get_buffer_state(buffer_id);
match buffer_state {
PresentationBufferState::Unassigned => {
// throw away all work, because we are from old swapchain
return;
},
PresentationBufferState::Mapping => {},
_ => panic!("Unexpected presentation buffer state"),
}
if config_changed {
/*
This means that while mapasync was running, context got recreated
so we need to throw all out work away.
It is also possible that we got recreated with same config,
so canvas should be cleared, but we handle such case in gpucanvascontext
with drawing_buffer.cleared
One more case is that we already have newer map async done,
so we can replace new image with old image but that should happen very rarely
One possible solution to all problems is blocking device timeline
(wgpu thread or introduce new timeline/thread for presentation)
something like this is also mentioned in spec:
2. Ensure that all submitted work items (e.g. queue submissions) have completed writing to the image
https://gpuweb.github.io/gpuweb/#abstract-opdef-get-a-copy-of-the-image-contents-of-a-context
*/
let _ = global.buffer_unmap(buffer_id);
*buffer_state = PresentationBufferState::Available;
return;
}
*buffer_state = PresentationBufferState::Mapped; *buffer_state = PresentationBufferState::Mapped;
let presentation_buffer = let presentation_buffer =
GPUPresentationBuffer::new(global, buffer_id, buffer_size); GPUPresentationBuffer::new(global, buffer_id, image_desc.buffer_size());
let old_presentation_buffer = present_data.data.replace(presentation_buffer); let Some(swap_chain) = context_data.swap_chain.as_mut() else {
return;
};
let old_presentation_buffer = swap_chain.data.replace(presentation_buffer);
let mut txn = Transaction::new(); let mut txn = Transaction::new();
txn.update_image( txn.update_image(
present_data.image_key, context_data.image_key,
present_data.image_desc, context_data.image_desc.0,
present_data.image_data.clone(), context_data.image_data.clone(),
&DirtyRect::All, &DirtyRect::All,
); );
webrender_api webrender_api
@ -425,10 +577,10 @@ fn update_wr_image(
.unwrap() .unwrap()
.send_transaction(webrender_document, txn); .send_transaction(webrender_document, txn);
if let Some(old_presentation_buffer) = old_presentation_buffer { if let Some(old_presentation_buffer) = old_presentation_buffer {
present_data.unmap_old_buffer(old_presentation_buffer) context_data.unmap_old_buffer(old_presentation_buffer)
} }
} else { } else {
error!("Data not found for {:?}", context_id); error!("WebGPU Context {:?} is destroyed", context_id);
} }
}, },
_ => error!("Could not map buffer({:?})", buffer_id), _ => error!("Could not map buffer({:?})", buffer_id),

View file

@ -6,7 +6,6 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::HashMap; use std::collections::HashMap;
use std::ops::ControlFlow;
use std::slice; use std::slice;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
@ -15,7 +14,7 @@ use ipc_channel::ipc::{IpcReceiver, IpcSender, IpcSharedMemory};
use log::{info, warn}; use log::{info, warn};
use servo_config::pref; use servo_config::pref;
use webrender::{RenderApi, RenderApiSender}; use webrender::{RenderApi, RenderApiSender};
use webrender_api::DocumentId; use webrender_api::{DocumentId, ExternalImageId};
use webrender_traits::{WebrenderExternalImageRegistry, WebrenderImageHandlerType}; use webrender_traits::{WebrenderExternalImageRegistry, WebrenderImageHandlerType};
use wgc::command::{ComputePass, ComputePassDescriptor, RenderPass}; use wgc::command::{ComputePass, ComputePassDescriptor, RenderPass};
use wgc::device::queue::SubmittedWorkDoneClosure; use wgc::device::queue::SubmittedWorkDoneClosure;
@ -404,17 +403,6 @@ impl WGPU {
self.maybe_dispatch_wgpu_error(device_id, error); self.maybe_dispatch_wgpu_error(device_id, error);
} }
}, },
WebGPURequest::CreateContext(sender) => {
let id = self
.external_images
.lock()
.expect("Lock poisoned?")
.next_id(WebrenderImageHandlerType::WebGPU);
let image_key = self.webrender_api.lock().unwrap().generate_image_key();
if let Err(e) = sender.send((WebGPUContextId(id.0), image_key)) {
warn!("Failed to send ExternalImageId to new context ({})", e);
};
},
WebGPURequest::CreatePipelineLayout { WebGPURequest::CreatePipelineLayout {
device_id, device_id,
pipeline_layout_id, pipeline_layout_id,
@ -513,17 +501,79 @@ impl WGPU {
} }
self.maybe_dispatch_wgpu_error(device_id, error); self.maybe_dispatch_wgpu_error(device_id, error);
}, },
WebGPURequest::CreateSwapChain { WebGPURequest::CreateContext {
device_id,
queue_id,
buffer_ids, buffer_ids,
context_id,
image_key,
size, size,
format, sender,
} => self.create_swapchain( } => {
device_id, queue_id, buffer_ids, context_id, format, size, image_key, let id = self
), .external_images
.lock()
.expect("Lock poisoned?")
.next_id(WebrenderImageHandlerType::WebGPU);
let image_key = self.webrender_api.lock().unwrap().generate_image_key();
let context_id = WebGPUContextId(id.0);
if let Err(e) = sender.send((context_id, image_key)) {
warn!("Failed to send ExternalImageId to new context ({})", e);
};
self.create_context(context_id, image_key, size, buffer_ids);
},
WebGPURequest::UpdateContext {
context_id,
size,
configuration,
} => {
self.update_context(context_id, size, configuration);
},
WebGPURequest::SwapChainPresent {
context_id,
texture_id,
encoder_id,
} => {
let result = self.swapchain_present(context_id, encoder_id, texture_id);
if let Err(e) = result {
log::error!("Error occured in SwapChainPresent: {e:?}");
}
},
WebGPURequest::ValidateTextureDescriptor {
device_id,
texture_id,
descriptor,
} => {
// https://gpuweb.github.io/gpuweb/#dom-gpucanvascontext-configure
// validating TextureDescriptor by creating dummy texture
let global = &self.global;
let (_, error) =
global.device_create_texture(device_id, &descriptor, Some(texture_id));
global.texture_drop(texture_id);
self.poller.wake();
if let Err(e) = self.script_sender.send(WebGPUMsg::FreeTexture(texture_id))
{
warn!("Unable to send FreeTexture({:?}) ({:?})", texture_id, e);
};
if let Some(error) = error {
self.dispatch_error(device_id, Error::from_error(error));
continue;
}
// Supported context formats
// TODO: wgt::TextureFormat::Rgba16Float, when wr supports HDR
if !matches!(
descriptor.format,
wgt::TextureFormat::Bgra8Unorm | wgt::TextureFormat::Rgba8Unorm
) {
self.dispatch_error(
device_id,
Error::Validation("Unsupported context format".to_string()),
);
}
},
WebGPURequest::DestroyContext { context_id } => {
self.destroy_context(context_id);
self.external_images
.lock()
.expect("Lock poisoned?")
.remove(&ExternalImageId(context_id.0));
},
WebGPURequest::CreateTexture { WebGPURequest::CreateTexture {
device_id, device_id,
texture_id, texture_id,
@ -561,12 +611,6 @@ impl WGPU {
// Wake poller thread to trigger DeviceLostClosure // Wake poller thread to trigger DeviceLostClosure
self.poller.wake(); self.poller.wake();
}, },
WebGPURequest::DestroySwapChain {
context_id,
image_key,
} => {
self.destroy_swapchain(context_id, image_key);
},
WebGPURequest::DestroyTexture(texture_id) => { WebGPURequest::DestroyTexture(texture_id) => {
let global = &self.global; let global = &self.global;
let _ = global.texture_destroy(texture_id); let _ = global.texture_destroy(texture_id);
@ -965,17 +1009,6 @@ impl WGPU {
}; };
self.maybe_dispatch_error(device_id, result.err()); self.maybe_dispatch_error(device_id, result.err());
}, },
WebGPURequest::SwapChainPresent {
context_id,
texture_id,
encoder_id,
} => {
if let ControlFlow::Break(_) =
self.swapchain_present(context_id, encoder_id, texture_id)
{
continue;
}
},
WebGPURequest::UnmapBuffer { WebGPURequest::UnmapBuffer {
buffer_id, buffer_id,
array_buffer, array_buffer,

View file

@ -9741,8 +9741,6 @@
[:format="bc1-rgba-unorm";canvasType="onscreen";enable_required_feature=false] [:format="bc1-rgba-unorm";canvasType="onscreen";enable_required_feature=false]
[:format="bc1-rgba-unorm";canvasType="onscreen";enable_required_feature=true] [:format="bc1-rgba-unorm";canvasType="onscreen";enable_required_feature=true]
expected:
if os == "linux" and not debug: FAIL
[:format="bc1-rgba-unorm-srgb";canvasType="offscreen";enable_required_feature=false] [:format="bc1-rgba-unorm-srgb";canvasType="offscreen";enable_required_feature=false]
@ -9751,8 +9749,6 @@
[:format="bc1-rgba-unorm-srgb";canvasType="onscreen";enable_required_feature=false] [:format="bc1-rgba-unorm-srgb";canvasType="onscreen";enable_required_feature=false]
[:format="bc1-rgba-unorm-srgb";canvasType="onscreen";enable_required_feature=true] [:format="bc1-rgba-unorm-srgb";canvasType="onscreen";enable_required_feature=true]
expected:
if os == "linux" and not debug: FAIL
[:format="bc2-rgba-unorm";canvasType="offscreen";enable_required_feature=false] [:format="bc2-rgba-unorm";canvasType="offscreen";enable_required_feature=false]
@ -9761,8 +9757,6 @@
[:format="bc2-rgba-unorm";canvasType="onscreen";enable_required_feature=false] [:format="bc2-rgba-unorm";canvasType="onscreen";enable_required_feature=false]
[:format="bc2-rgba-unorm";canvasType="onscreen";enable_required_feature=true] [:format="bc2-rgba-unorm";canvasType="onscreen";enable_required_feature=true]
expected:
if os == "linux" and not debug: FAIL
[:format="bc2-rgba-unorm-srgb";canvasType="offscreen";enable_required_feature=false] [:format="bc2-rgba-unorm-srgb";canvasType="offscreen";enable_required_feature=false]
@ -9771,8 +9765,6 @@
[:format="bc2-rgba-unorm-srgb";canvasType="onscreen";enable_required_feature=false] [:format="bc2-rgba-unorm-srgb";canvasType="onscreen";enable_required_feature=false]
[:format="bc2-rgba-unorm-srgb";canvasType="onscreen";enable_required_feature=true] [:format="bc2-rgba-unorm-srgb";canvasType="onscreen";enable_required_feature=true]
expected:
if os == "linux" and not debug: FAIL
[:format="bc3-rgba-unorm";canvasType="offscreen";enable_required_feature=false] [:format="bc3-rgba-unorm";canvasType="offscreen";enable_required_feature=false]
@ -9781,8 +9773,6 @@
[:format="bc3-rgba-unorm";canvasType="onscreen";enable_required_feature=false] [:format="bc3-rgba-unorm";canvasType="onscreen";enable_required_feature=false]
[:format="bc3-rgba-unorm";canvasType="onscreen";enable_required_feature=true] [:format="bc3-rgba-unorm";canvasType="onscreen";enable_required_feature=true]
expected:
if os == "linux" and not debug: FAIL
[:format="bc3-rgba-unorm-srgb";canvasType="offscreen";enable_required_feature=false] [:format="bc3-rgba-unorm-srgb";canvasType="offscreen";enable_required_feature=false]
@ -9791,8 +9781,6 @@
[:format="bc3-rgba-unorm-srgb";canvasType="onscreen";enable_required_feature=false] [:format="bc3-rgba-unorm-srgb";canvasType="onscreen";enable_required_feature=false]
[:format="bc3-rgba-unorm-srgb";canvasType="onscreen";enable_required_feature=true] [:format="bc3-rgba-unorm-srgb";canvasType="onscreen";enable_required_feature=true]
expected:
if os == "linux" and not debug: FAIL
[:format="bc4-r-snorm";canvasType="offscreen";enable_required_feature=false] [:format="bc4-r-snorm";canvasType="offscreen";enable_required_feature=false]
@ -9801,8 +9789,6 @@
[:format="bc4-r-snorm";canvasType="onscreen";enable_required_feature=false] [:format="bc4-r-snorm";canvasType="onscreen";enable_required_feature=false]
[:format="bc4-r-snorm";canvasType="onscreen";enable_required_feature=true] [:format="bc4-r-snorm";canvasType="onscreen";enable_required_feature=true]
expected:
if os == "linux" and not debug: FAIL
[:format="bc4-r-unorm";canvasType="offscreen";enable_required_feature=false] [:format="bc4-r-unorm";canvasType="offscreen";enable_required_feature=false]
@ -9811,8 +9797,6 @@
[:format="bc4-r-unorm";canvasType="onscreen";enable_required_feature=false] [:format="bc4-r-unorm";canvasType="onscreen";enable_required_feature=false]
[:format="bc4-r-unorm";canvasType="onscreen";enable_required_feature=true] [:format="bc4-r-unorm";canvasType="onscreen";enable_required_feature=true]
expected:
if os == "linux" and not debug: FAIL
[:format="bc5-rg-snorm";canvasType="offscreen";enable_required_feature=false] [:format="bc5-rg-snorm";canvasType="offscreen";enable_required_feature=false]
@ -9821,8 +9805,6 @@
[:format="bc5-rg-snorm";canvasType="onscreen";enable_required_feature=false] [:format="bc5-rg-snorm";canvasType="onscreen";enable_required_feature=false]
[:format="bc5-rg-snorm";canvasType="onscreen";enable_required_feature=true] [:format="bc5-rg-snorm";canvasType="onscreen";enable_required_feature=true]
expected:
if os == "linux" and not debug: FAIL
[:format="bc5-rg-unorm";canvasType="offscreen";enable_required_feature=false] [:format="bc5-rg-unorm";canvasType="offscreen";enable_required_feature=false]
@ -9831,8 +9813,6 @@
[:format="bc5-rg-unorm";canvasType="onscreen";enable_required_feature=false] [:format="bc5-rg-unorm";canvasType="onscreen";enable_required_feature=false]
[:format="bc5-rg-unorm";canvasType="onscreen";enable_required_feature=true] [:format="bc5-rg-unorm";canvasType="onscreen";enable_required_feature=true]
expected:
if os == "linux" and not debug: FAIL
[:format="bc6h-rgb-float";canvasType="offscreen";enable_required_feature=false] [:format="bc6h-rgb-float";canvasType="offscreen";enable_required_feature=false]
@ -9841,8 +9821,6 @@
[:format="bc6h-rgb-float";canvasType="onscreen";enable_required_feature=false] [:format="bc6h-rgb-float";canvasType="onscreen";enable_required_feature=false]
[:format="bc6h-rgb-float";canvasType="onscreen";enable_required_feature=true] [:format="bc6h-rgb-float";canvasType="onscreen";enable_required_feature=true]
expected:
if os == "linux" and not debug: FAIL
[:format="bc6h-rgb-ufloat";canvasType="offscreen";enable_required_feature=false] [:format="bc6h-rgb-ufloat";canvasType="offscreen";enable_required_feature=false]
@ -9851,8 +9829,6 @@
[:format="bc6h-rgb-ufloat";canvasType="onscreen";enable_required_feature=false] [:format="bc6h-rgb-ufloat";canvasType="onscreen";enable_required_feature=false]
[:format="bc6h-rgb-ufloat";canvasType="onscreen";enable_required_feature=true] [:format="bc6h-rgb-ufloat";canvasType="onscreen";enable_required_feature=true]
expected:
if os == "linux" and not debug: FAIL
[:format="bc7-rgba-unorm";canvasType="offscreen";enable_required_feature=false] [:format="bc7-rgba-unorm";canvasType="offscreen";enable_required_feature=false]
@ -9861,8 +9837,6 @@
[:format="bc7-rgba-unorm";canvasType="onscreen";enable_required_feature=false] [:format="bc7-rgba-unorm";canvasType="onscreen";enable_required_feature=false]
[:format="bc7-rgba-unorm";canvasType="onscreen";enable_required_feature=true] [:format="bc7-rgba-unorm";canvasType="onscreen";enable_required_feature=true]
expected:
if os == "linux" and not debug: FAIL
[:format="bc7-rgba-unorm-srgb";canvasType="offscreen";enable_required_feature=false] [:format="bc7-rgba-unorm-srgb";canvasType="offscreen";enable_required_feature=false]
@ -9871,8 +9845,6 @@
[:format="bc7-rgba-unorm-srgb";canvasType="onscreen";enable_required_feature=false] [:format="bc7-rgba-unorm-srgb";canvasType="onscreen";enable_required_feature=false]
[:format="bc7-rgba-unorm-srgb";canvasType="onscreen";enable_required_feature=true] [:format="bc7-rgba-unorm-srgb";canvasType="onscreen";enable_required_feature=true]
expected:
if os == "linux" and not debug: FAIL
[:format="depth32float-stencil8";canvasType="offscreen";enable_required_feature=false] [:format="depth32float-stencil8";canvasType="offscreen";enable_required_feature=false]
@ -9881,8 +9853,6 @@
[:format="depth32float-stencil8";canvasType="onscreen";enable_required_feature=false] [:format="depth32float-stencil8";canvasType="onscreen";enable_required_feature=false]
[:format="depth32float-stencil8";canvasType="onscreen";enable_required_feature=true] [:format="depth32float-stencil8";canvasType="onscreen";enable_required_feature=true]
expected:
if os == "linux" and not debug: FAIL
[:format="eac-r11snorm";canvasType="offscreen";enable_required_feature=false] [:format="eac-r11snorm";canvasType="offscreen";enable_required_feature=false]
@ -16934,9 +16904,45 @@
[cts.https.html?q=webgpu:api,validation,capability_checks,limits,maxTextureDimension2D:configure,at_over:*] [cts.https.html?q=webgpu:api,validation,capability_checks,limits,maxTextureDimension2D:configure,at_over:*]
disabled: true [:limitTest="atDefault";testValueName="atLimit";canvasType="offscreen"]
expected:
if os == "linux" and not debug: SKIP [:limitTest="atDefault";testValueName="atLimit";canvasType="onscreen"]
[:limitTest="atDefault";testValueName="overLimit";canvasType="offscreen"]
[:limitTest="atDefault";testValueName="overLimit";canvasType="onscreen"]
[:limitTest="atMaximum";testValueName="atLimit";canvasType="offscreen"]
[:limitTest="atMaximum";testValueName="atLimit";canvasType="onscreen"]
[:limitTest="atMaximum";testValueName="overLimit";canvasType="offscreen"]
[:limitTest="atMaximum";testValueName="overLimit";canvasType="onscreen"]
[:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";canvasType="offscreen"]
[:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";canvasType="onscreen"]
[:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";canvasType="offscreen"]
[:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";canvasType="onscreen"]
[:limitTest="overMaximum";testValueName="atLimit";canvasType="offscreen"]
[:limitTest="overMaximum";testValueName="atLimit";canvasType="onscreen"]
[:limitTest="overMaximum";testValueName="overLimit";canvasType="offscreen"]
[:limitTest="overMaximum";testValueName="overLimit";canvasType="onscreen"]
[:limitTest="underDefault";testValueName="atLimit";canvasType="offscreen"]
[:limitTest="underDefault";testValueName="atLimit";canvasType="onscreen"]
[:limitTest="underDefault";testValueName="overLimit";canvasType="offscreen"]
[:limitTest="underDefault";testValueName="overLimit";canvasType="onscreen"]
[cts.https.html?q=webgpu:api,validation,capability_checks,limits,maxTextureDimension2D:createTexture,at_over:*] [cts.https.html?q=webgpu:api,validation,capability_checks,limits,maxTextureDimension2D:createTexture,at_over:*]
@ -16962,8 +16968,45 @@
[cts.https.html?q=webgpu:api,validation,capability_checks,limits,maxTextureDimension2D:getCurrentTexture,at_over:*] [cts.https.html?q=webgpu:api,validation,capability_checks,limits,maxTextureDimension2D:getCurrentTexture,at_over:*]
expected: [:limitTest="atDefault";testValueName="atLimit";canvasType="offscreen"]
if os == "linux" and not debug: CRASH
[:limitTest="atDefault";testValueName="atLimit";canvasType="onscreen"]
[:limitTest="atDefault";testValueName="overLimit";canvasType="offscreen"]
[:limitTest="atDefault";testValueName="overLimit";canvasType="onscreen"]
[:limitTest="atMaximum";testValueName="atLimit";canvasType="offscreen"]
[:limitTest="atMaximum";testValueName="atLimit";canvasType="onscreen"]
[:limitTest="atMaximum";testValueName="overLimit";canvasType="offscreen"]
[:limitTest="atMaximum";testValueName="overLimit";canvasType="onscreen"]
[:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";canvasType="offscreen"]
[:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";canvasType="onscreen"]
[:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";canvasType="offscreen"]
[:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";canvasType="onscreen"]
[:limitTest="overMaximum";testValueName="atLimit";canvasType="offscreen"]
[:limitTest="overMaximum";testValueName="atLimit";canvasType="onscreen"]
[:limitTest="overMaximum";testValueName="overLimit";canvasType="offscreen"]
[:limitTest="overMaximum";testValueName="overLimit";canvasType="onscreen"]
[:limitTest="underDefault";testValueName="atLimit";canvasType="offscreen"]
[:limitTest="underDefault";testValueName="atLimit";canvasType="onscreen"]
[:limitTest="underDefault";testValueName="overLimit";canvasType="offscreen"]
[:limitTest="underDefault";testValueName="overLimit";canvasType="onscreen"]
[cts.https.html?q=webgpu:api,validation,capability_checks,limits,maxTextureDimension3D:createTexture,at_over:*] [cts.https.html?q=webgpu:api,validation,capability_checks,limits,maxTextureDimension3D:createTexture,at_over:*]
@ -185733,60 +185776,32 @@
[:canvasType="onscreen";format="astc-8x8-unorm-srgb"] [:canvasType="onscreen";format="astc-8x8-unorm-srgb"]
[:canvasType="onscreen";format="bc1-rgba-unorm"] [:canvasType="onscreen";format="bc1-rgba-unorm"]
expected:
if os == "linux" and not debug: FAIL
[:canvasType="onscreen";format="bc1-rgba-unorm-srgb"] [:canvasType="onscreen";format="bc1-rgba-unorm-srgb"]
expected:
if os == "linux" and not debug: FAIL
[:canvasType="onscreen";format="bc2-rgba-unorm"] [:canvasType="onscreen";format="bc2-rgba-unorm"]
expected:
if os == "linux" and not debug: FAIL
[:canvasType="onscreen";format="bc2-rgba-unorm-srgb"] [:canvasType="onscreen";format="bc2-rgba-unorm-srgb"]
expected:
if os == "linux" and not debug: FAIL
[:canvasType="onscreen";format="bc3-rgba-unorm"] [:canvasType="onscreen";format="bc3-rgba-unorm"]
expected:
if os == "linux" and not debug: FAIL
[:canvasType="onscreen";format="bc3-rgba-unorm-srgb"] [:canvasType="onscreen";format="bc3-rgba-unorm-srgb"]
expected:
if os == "linux" and not debug: FAIL
[:canvasType="onscreen";format="bc4-r-snorm"] [:canvasType="onscreen";format="bc4-r-snorm"]
expected:
if os == "linux" and not debug: FAIL
[:canvasType="onscreen";format="bc4-r-unorm"] [:canvasType="onscreen";format="bc4-r-unorm"]
expected:
if os == "linux" and not debug: FAIL
[:canvasType="onscreen";format="bc5-rg-snorm"] [:canvasType="onscreen";format="bc5-rg-snorm"]
expected:
if os == "linux" and not debug: FAIL
[:canvasType="onscreen";format="bc5-rg-unorm"] [:canvasType="onscreen";format="bc5-rg-unorm"]
expected:
if os == "linux" and not debug: FAIL
[:canvasType="onscreen";format="bc6h-rgb-float"] [:canvasType="onscreen";format="bc6h-rgb-float"]
expected:
if os == "linux" and not debug: FAIL
[:canvasType="onscreen";format="bc6h-rgb-ufloat"] [:canvasType="onscreen";format="bc6h-rgb-ufloat"]
expected:
if os == "linux" and not debug: FAIL
[:canvasType="onscreen";format="bc7-rgba-unorm"] [:canvasType="onscreen";format="bc7-rgba-unorm"]
expected:
if os == "linux" and not debug: FAIL
[:canvasType="onscreen";format="bc7-rgba-unorm-srgb"] [:canvasType="onscreen";format="bc7-rgba-unorm-srgb"]
expected:
if os == "linux" and not debug: FAIL
[:canvasType="onscreen";format="bgra8unorm"] [:canvasType="onscreen";format="bgra8unorm"]
@ -185863,8 +185878,6 @@
if os == "linux" and not debug: FAIL if os == "linux" and not debug: FAIL
[:canvasType="onscreen";format="r8snorm"] [:canvasType="onscreen";format="r8snorm"]
expected:
if os == "linux" and not debug: FAIL
[:canvasType="onscreen";format="r8uint"] [:canvasType="onscreen";format="r8uint"]
expected: expected:
@ -185875,8 +185888,6 @@
if os == "linux" and not debug: FAIL if os == "linux" and not debug: FAIL
[:canvasType="onscreen";format="rg11b10ufloat"] [:canvasType="onscreen";format="rg11b10ufloat"]
expected:
if os == "linux" and not debug: FAIL
[:canvasType="onscreen";format="rg16float"] [:canvasType="onscreen";format="rg16float"]
expected: expected:
@ -185907,8 +185918,6 @@
if os == "linux" and not debug: FAIL if os == "linux" and not debug: FAIL
[:canvasType="onscreen";format="rg8snorm"] [:canvasType="onscreen";format="rg8snorm"]
expected:
if os == "linux" and not debug: FAIL
[:canvasType="onscreen";format="rg8uint"] [:canvasType="onscreen";format="rg8uint"]
expected: expected:
@ -185927,8 +185936,6 @@
if os == "linux" and not debug: FAIL if os == "linux" and not debug: FAIL
[:canvasType="onscreen";format="rgb9e5ufloat"] [:canvasType="onscreen";format="rgb9e5ufloat"]
expected:
if os == "linux" and not debug: FAIL
[:canvasType="onscreen";format="rgba16float"] [:canvasType="onscreen";format="rgba16float"]
expected: expected:
@ -185959,8 +185966,6 @@
if os == "linux" and not debug: FAIL if os == "linux" and not debug: FAIL
[:canvasType="onscreen";format="rgba8snorm"] [:canvasType="onscreen";format="rgba8snorm"]
expected:
if os == "linux" and not debug: FAIL
[:canvasType="onscreen";format="rgba8uint"] [:canvasType="onscreen";format="rgba8uint"]
expected: expected:
@ -185978,13 +185983,23 @@
[cts.https.html?q=webgpu:web_platform,canvas,configure:size_zero_after_configure:*] [cts.https.html?q=webgpu:web_platform,canvas,configure:size_zero_after_configure:*]
expected: [:canvasType="offscreen";zeroDimension="height"]
if os == "linux" and not debug: CRASH
[:canvasType="offscreen";zeroDimension="width"]
[:canvasType="onscreen";zeroDimension="height"]
[:canvasType="onscreen";zeroDimension="width"]
[cts.https.html?q=webgpu:web_platform,canvas,configure:size_zero_before_configure:*] [cts.https.html?q=webgpu:web_platform,canvas,configure:size_zero_before_configure:*]
expected: [:canvasType="offscreen";zeroDimension="height"]
if os == "linux" and not debug: CRASH
[:canvasType="offscreen";zeroDimension="width"]
[:canvasType="onscreen";zeroDimension="height"]
[:canvasType="onscreen";zeroDimension="width"]
[cts.https.html?q=webgpu:web_platform,canvas,configure:usage:*] [cts.https.html?q=webgpu:web_platform,canvas,configure:usage:*]
@ -186027,18 +186042,12 @@
[:canvasType="offscreen";format="rgba8unorm";viewFormatFeature="texture-compression-etc2"] [:canvasType="offscreen";format="rgba8unorm";viewFormatFeature="texture-compression-etc2"]
[:canvasType="onscreen";format="bgra8unorm";viewFormatFeature="_undef_"] [:canvasType="onscreen";format="bgra8unorm";viewFormatFeature="_undef_"]
expected:
if os == "linux" and not debug: FAIL
[:canvasType="onscreen";format="bgra8unorm";viewFormatFeature="depth32float-stencil8"] [:canvasType="onscreen";format="bgra8unorm";viewFormatFeature="depth32float-stencil8"]
expected:
if os == "linux" and not debug: FAIL
[:canvasType="onscreen";format="bgra8unorm";viewFormatFeature="texture-compression-astc"] [:canvasType="onscreen";format="bgra8unorm";viewFormatFeature="texture-compression-astc"]
[:canvasType="onscreen";format="bgra8unorm";viewFormatFeature="texture-compression-bc"] [:canvasType="onscreen";format="bgra8unorm";viewFormatFeature="texture-compression-bc"]
expected:
if os == "linux" and not debug: FAIL
[:canvasType="onscreen";format="bgra8unorm";viewFormatFeature="texture-compression-etc2"] [:canvasType="onscreen";format="bgra8unorm";viewFormatFeature="texture-compression-etc2"]
@ -186047,30 +186056,20 @@
if os == "linux" and not debug: FAIL if os == "linux" and not debug: FAIL
[:canvasType="onscreen";format="rgba16float";viewFormatFeature="depth32float-stencil8"] [:canvasType="onscreen";format="rgba16float";viewFormatFeature="depth32float-stencil8"]
expected:
if os == "linux" and not debug: FAIL
[:canvasType="onscreen";format="rgba16float";viewFormatFeature="texture-compression-astc"] [:canvasType="onscreen";format="rgba16float";viewFormatFeature="texture-compression-astc"]
[:canvasType="onscreen";format="rgba16float";viewFormatFeature="texture-compression-bc"] [:canvasType="onscreen";format="rgba16float";viewFormatFeature="texture-compression-bc"]
expected:
if os == "linux" and not debug: FAIL
[:canvasType="onscreen";format="rgba16float";viewFormatFeature="texture-compression-etc2"] [:canvasType="onscreen";format="rgba16float";viewFormatFeature="texture-compression-etc2"]
[:canvasType="onscreen";format="rgba8unorm";viewFormatFeature="_undef_"] [:canvasType="onscreen";format="rgba8unorm";viewFormatFeature="_undef_"]
expected:
if os == "linux" and not debug: FAIL
[:canvasType="onscreen";format="rgba8unorm";viewFormatFeature="depth32float-stencil8"] [:canvasType="onscreen";format="rgba8unorm";viewFormatFeature="depth32float-stencil8"]
expected:
if os == "linux" and not debug: FAIL
[:canvasType="onscreen";format="rgba8unorm";viewFormatFeature="texture-compression-astc"] [:canvasType="onscreen";format="rgba8unorm";viewFormatFeature="texture-compression-astc"]
[:canvasType="onscreen";format="rgba8unorm";viewFormatFeature="texture-compression-bc"] [:canvasType="onscreen";format="rgba8unorm";viewFormatFeature="texture-compression-bc"]
expected:
if os == "linux" and not debug: FAIL
[:canvasType="onscreen";format="rgba8unorm";viewFormatFeature="texture-compression-etc2"] [:canvasType="onscreen";format="rgba8unorm";viewFormatFeature="texture-compression-etc2"]
@ -186082,8 +186081,9 @@
[cts.https.html?q=webgpu:web_platform,canvas,getCurrentTexture:configured:*] [cts.https.html?q=webgpu:web_platform,canvas,getCurrentTexture:configured:*]
expected: [:canvasType="offscreen"]
if os == "linux" and not debug: CRASH
[:canvasType="onscreen"]
[cts.https.html?q=webgpu:web_platform,canvas,getCurrentTexture:expiry:*] [cts.https.html?q=webgpu:web_platform,canvas,getCurrentTexture:expiry:*]
@ -186119,8 +186119,9 @@
[cts.https.html?q=webgpu:web_platform,canvas,getCurrentTexture:resize:*] [cts.https.html?q=webgpu:web_platform,canvas,getCurrentTexture:resize:*]
expected: [:canvasType="offscreen"]
if os == "linux" and not debug: CRASH
[:canvasType="onscreen"]
[cts.https.html?q=webgpu:web_platform,canvas,getCurrentTexture:single_frames:*] [cts.https.html?q=webgpu:web_platform,canvas,getCurrentTexture:single_frames:*]

View file

@ -1,6 +1,3 @@
[canvas_clear.https.html] [canvas_clear.https.html]
expected: expected:
if os == "win": CRASH if os == "linux" and not debug: FAIL
if os == "linux" and debug: CRASH
if os == "linux" and not debug: TIMEOUT
if os == "mac": CRASH

View file

@ -1,6 +1,3 @@
[canvas_colorspace_rgba16float.https.html] [canvas_colorspace_rgba16float.https.html]
expected: expected:
if os == "win": CRASH
if os == "linux" and debug: CRASH
if os == "linux" and not debug: TIMEOUT if os == "linux" and not debug: TIMEOUT
if os == "mac": CRASH

View file

@ -1,6 +1,3 @@
[canvas_complex_rgba16float_copy.https.html] [canvas_complex_rgba16float_copy.https.html]
expected: expected:
if os == "win": CRASH
if os == "linux" and debug: CRASH
if os == "linux" and not debug: TIMEOUT if os == "linux" and not debug: TIMEOUT
if os == "mac": CRASH

View file

@ -1,6 +1,3 @@
[canvas_complex_rgba16float_draw.https.html] [canvas_complex_rgba16float_draw.https.html]
expected: expected:
if os == "win": CRASH
if os == "linux" and debug: CRASH
if os == "linux" and not debug: TIMEOUT if os == "linux" and not debug: TIMEOUT
if os == "mac": CRASH

View file

@ -1,6 +1,3 @@
[canvas_complex_rgba16float_store.https.html] [canvas_complex_rgba16float_store.https.html]
expected: expected:
if os == "win": CRASH if os == "linux" and not debug: FAIL
if os == "linux" and debug: CRASH
if os == "linux" and not debug: TIMEOUT
if os == "mac": CRASH

View file

@ -1,3 +1,3 @@
[canvas_complex_rgba8unorm_store.https.html] [canvas_complex_rgba8unorm_store.https.html]
expected: expected:
if os == "linux" and not debug: [PASS, FAIL] if os == "linux" and not debug: PASS

View file

@ -1,3 +1,3 @@
[canvas_composite_alpha_bgra8unorm_opaque_copy.https.html] [canvas_composite_alpha_bgra8unorm_opaque_copy.https.html]
expected: expected:
if os == "linux" and not debug: [CRASH, PASS, FAIL] if os == "linux" and not debug: PASS

View file

@ -1,2 +1,2 @@
[canvas_composite_alpha_bgra8unorm_opaque_draw.https.html] [canvas_composite_alpha_bgra8unorm_opaque_draw.https.html]
expected: [CRASH, PASS, FAIL] expected: PASS

View file

@ -1,2 +1,3 @@
[canvas_composite_alpha_bgra8unorm_premultiplied_copy.https.html] [canvas_composite_alpha_bgra8unorm_premultiplied_copy.https.html]
expected: FAIL expected:
if os == "linux" and not debug: PASS

View file

@ -1,2 +1,3 @@
[canvas_composite_alpha_bgra8unorm_premultiplied_draw.https.html] [canvas_composite_alpha_bgra8unorm_premultiplied_draw.https.html]
expected: FAIL expected:
if os == "linux" and not debug: PASS

View file

@ -1,6 +1,3 @@
[canvas_composite_alpha_rgba16float_opaque_copy.https.html] [canvas_composite_alpha_rgba16float_opaque_copy.https.html]
expected: expected:
if os == "win": CRASH if os == "linux" and not debug: FAIL
if os == "linux" and debug: CRASH
if os == "linux" and not debug: TIMEOUT
if os == "mac": CRASH

View file

@ -1,6 +1,3 @@
[canvas_composite_alpha_rgba16float_opaque_draw.https.html] [canvas_composite_alpha_rgba16float_opaque_draw.https.html]
expected: expected:
if os == "win": CRASH if os == "linux" and not debug: FAIL
if os == "linux" and debug: CRASH
if os == "linux" and not debug: TIMEOUT
if os == "mac": CRASH

View file

@ -1,6 +1,3 @@
[canvas_composite_alpha_rgba16float_premultiplied_copy.https.html] [canvas_composite_alpha_rgba16float_premultiplied_copy.https.html]
expected: expected:
if os == "win": CRASH if os == "linux" and not debug: FAIL
if os == "linux" and debug: CRASH
if os == "linux" and not debug: TIMEOUT
if os == "mac": CRASH

View file

@ -1,6 +1,3 @@
[canvas_composite_alpha_rgba16float_premultiplied_draw.https.html] [canvas_composite_alpha_rgba16float_premultiplied_draw.https.html]
expected: expected:
if os == "win": CRASH if os == "linux" and not debug: FAIL
if os == "linux" and debug: CRASH
if os == "linux" and not debug: TIMEOUT
if os == "mac": CRASH

View file

@ -1,3 +1,3 @@
[canvas_composite_alpha_rgba8unorm_opaque_copy.https.html] [canvas_composite_alpha_rgba8unorm_opaque_copy.https.html]
expected: expected:
if os == "linux" and not debug: [CRASH, PASS, FAIL] if os == "linux" and not debug: PASS

View file

@ -1,3 +1,3 @@
[canvas_composite_alpha_rgba8unorm_opaque_draw.https.html] [canvas_composite_alpha_rgba8unorm_opaque_draw.https.html]
expected: expected:
if os == "linux" and not debug: [PASS, FAIL] if os == "linux" and not debug: PASS

View file

@ -1,2 +1,3 @@
[canvas_composite_alpha_rgba8unorm_premultiplied_copy.https.html] [canvas_composite_alpha_rgba8unorm_premultiplied_copy.https.html]
expected: FAIL expected:
if os == "linux" and not debug: PASS

View file

@ -1,2 +1,3 @@
[canvas_composite_alpha_rgba8unorm_premultiplied_draw.https.html] [canvas_composite_alpha_rgba8unorm_premultiplied_draw.https.html]
expected: [CRASH, FAIL] expected:
if os == "linux" and not debug: PASS

View file

@ -1,6 +1,3 @@
[delay_get_texture.https.html] [delay_get_texture.https.html]
expected: expected:
if os == "win": FAIL if os == "linux" and not debug: PASS
if os == "linux" and debug: FAIL
if os == "linux" and not debug: [PASS, FAIL]
if os == "mac": FAIL