mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
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:
parent
0b2549f4cb
commit
05ecb8eddb
30 changed files with 795 additions and 483 deletions
|
@ -2951,7 +2951,7 @@ impl Document {
|
|||
self.dirty_webgpu_contexts
|
||||
.borrow_mut()
|
||||
.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>>>> {
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
|
||||
use arrayvec::ArrayVec;
|
||||
use dom_struct::dom_struct;
|
||||
use euclid::default::Size2D;
|
||||
|
@ -9,20 +12,25 @@ use ipc_channel::ipc;
|
|||
use script_layout_interface::HTMLCanvasDataSource;
|
||||
use webgpu::swapchain::WebGPUContextId;
|
||||
use webgpu::wgc::id;
|
||||
use webgpu::{WebGPU, WebGPURequest, WebGPUTexture, PRESENTATION_BUFFER_COUNT};
|
||||
use webrender_api::{units, ImageFormat, ImageKey};
|
||||
use webgpu::{
|
||||
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::error::{Error, Fallible};
|
||||
use super::bindings::root::MutNullableDom;
|
||||
use super::bindings::str::USVString;
|
||||
use super::gpuconvert::convert_texture_descriptor;
|
||||
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::{
|
||||
GPUCanvasConfiguration, GPUCanvasContextMethods, GPUDeviceMethods, GPUExtent3D,
|
||||
GPUExtent3DDict, GPUObjectDescriptorBase, GPUTextureDescriptor, GPUTextureDimension,
|
||||
GPUTextureFormat,
|
||||
};
|
||||
use crate::dom::bindings::inheritance::Castable;
|
||||
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]
|
||||
pub struct GPUCanvasContext {
|
||||
reflector_: Reflector,
|
||||
|
@ -99,14 +130,34 @@ pub struct GPUCanvasContext {
|
|||
webrender_image: ImageKey,
|
||||
#[no_trace]
|
||||
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>
|
||||
texture: MutNullableDom<GPUTexture>,
|
||||
current_texture: MutNullableDom<GPUTexture>,
|
||||
}
|
||||
|
||||
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();
|
||||
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);
|
||||
}
|
||||
let (external_id, webrender_image) = receiver.recv().unwrap();
|
||||
|
@ -116,13 +167,21 @@ impl GPUCanvasContext {
|
|||
canvas,
|
||||
webrender_image,
|
||||
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> {
|
||||
reflect_dom_object(
|
||||
Box::new(GPUCanvasContext::new_inherited(
|
||||
global,
|
||||
HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(DomRoot::from_ref(canvas)),
|
||||
channel,
|
||||
)),
|
||||
|
@ -131,17 +190,93 @@ impl GPUCanvasContext {
|
|||
}
|
||||
}
|
||||
|
||||
// Abstract ops from spec
|
||||
impl GPUCanvasContext {
|
||||
fn layout_handle(&self) -> HTMLCanvasDataSource {
|
||||
HTMLCanvasDataSource::WebGPU(self.webrender_image)
|
||||
/// <https://gpuweb.github.io/gpuweb/#abstract-opdef-gputexturedescriptor-for-the-canvas-and-configuration>
|
||||
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) {
|
||||
let texture_id = self.texture_id().unwrap().0;
|
||||
/// <https://gpuweb.github.io/gpuweb/#abstract-opdef-expire-the-current-texture>
|
||||
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();
|
||||
if let Err(e) = self.channel.0.send(WebGPURequest::SwapChainPresent {
|
||||
context_id: self.context_id,
|
||||
texture_id,
|
||||
texture_id: texture_id.0,
|
||||
encoder_id,
|
||||
}) {
|
||||
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
|
||||
}
|
||||
|
||||
pub fn texture_id(&self) -> Option<WebGPUTexture> {
|
||||
self.texture.get().map(|t| t.id())
|
||||
}
|
||||
|
||||
pub fn mark_as_dirty(&self) {
|
||||
pub(crate) fn mark_as_dirty(&self) {
|
||||
if let HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) = &self.canvas {
|
||||
canvas.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
|
||||
let document = document_from_node(&**canvas);
|
||||
document.add_dirty_webgpu_canvas(self);
|
||||
}
|
||||
// TODO(sagudev): offscreen canvas also dirty?
|
||||
}
|
||||
|
||||
fn size(&self) -> Size2D<u64> {
|
||||
match &self.canvas {
|
||||
HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) => {
|
||||
Size2D::new(canvas.Width() as u64, canvas.Height() as u64)
|
||||
},
|
||||
HTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(canvas) => canvas.get_size(),
|
||||
/// <https://gpuweb.github.io/gpuweb/#abstract-opdef-updating-the-rendering-of-a-webgpu-canvas>
|
||||
pub(crate) fn update_rendering_of_webgpu_canvas(&self) {
|
||||
// Step 1
|
||||
self.expire_current_texture();
|
||||
}
|
||||
|
||||
/// <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>
|
||||
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
|
||||
let size = self.size();
|
||||
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,
|
||||
};
|
||||
let descriptor = self.texture_descriptor_for_canvas(configuration);
|
||||
|
||||
// Step 8
|
||||
let mut buffer_ids = ArrayVec::<id::BufferId, PRESENTATION_BUFFER_COUNT>::new();
|
||||
for _ in 0..PRESENTATION_BUFFER_COUNT {
|
||||
buffer_ids.push(self.global().wgpu_id_hub().create_buffer_id());
|
||||
}
|
||||
// Step 2&3
|
||||
let (mut desc, _) = convert_texture_descriptor(&descriptor, &configuration.device)?;
|
||||
desc.label = Some(Cow::Borrowed(
|
||||
"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
|
||||
.0
|
||||
.send(WebGPURequest::CreateSwapChain {
|
||||
.send(WebGPURequest::ValidateTextureDescriptor {
|
||||
device_id: configuration.device.id().0,
|
||||
queue_id: configuration.device.GetQueue().id().0,
|
||||
buffer_ids,
|
||||
context_id: self.context_id,
|
||||
image_key: self.webrender_image,
|
||||
format,
|
||||
size: units::DeviceIntSize::new(size.width as i32, size.height as i32),
|
||||
texture_id,
|
||||
descriptor: desc,
|
||||
})
|
||||
.expect("Failed to create WebGPU SwapChain");
|
||||
|
||||
self.texture.set(Some(
|
||||
&configuration.device.CreateTexture(&text_desc).unwrap(),
|
||||
));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// <https://gpuweb.github.io/gpuweb/#dom-gpucanvascontext-unconfigure>
|
||||
fn Unconfigure(&self) {
|
||||
if let Some(texture) = self.texture.take() {
|
||||
if let Err(e) = self.channel.0.send(WebGPURequest::DestroySwapChain {
|
||||
// Step 1
|
||||
self.configuration.take();
|
||||
// Step 2
|
||||
self.current_texture.take();
|
||||
// Step 3
|
||||
self.replace_drawing_buffer();
|
||||
}
|
||||
|
||||
/// <https://gpuweb.github.io/gpuweb/#dom-gpucanvascontext-getcurrenttexture>
|
||||
fn GetCurrentTexture(&self) -> Fallible<DomRoot<GPUTexture>> {
|
||||
// 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(¤t_texture));
|
||||
current_texture
|
||||
};
|
||||
// Step 5
|
||||
self.mark_as_dirty();
|
||||
// Step 6
|
||||
Ok(current_texture)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for GPUCanvasContext {
|
||||
fn drop(&mut self) {
|
||||
if let Err(e) = self.channel.0.send(WebGPURequest::DestroyContext {
|
||||
context_id: self.context_id,
|
||||
image_key: self.webrender_image,
|
||||
}) {
|
||||
warn!(
|
||||
"Failed to send DestroySwapChain-ImageKey({:?}) ({})",
|
||||
self.webrender_image, e
|
||||
);
|
||||
}
|
||||
drop(texture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://gpuweb.github.io/gpuweb/#dom-gpucanvascontext-getcurrenttexture>
|
||||
fn GetCurrentTexture(&self) -> Fallible<DomRoot<GPUTexture>> {
|
||||
// Step 5.
|
||||
self.mark_as_dirty();
|
||||
// Step 6.
|
||||
self.texture.get().ok_or(Error::InvalidState)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for GPUCanvasContext {
|
||||
fn drop(&mut self) {
|
||||
self.Unconfigure()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,10 +8,11 @@ use std::num::NonZeroU64;
|
|||
use webgpu::wgc::binding_model::{BindGroupEntry, BindingResource, BufferBinding};
|
||||
use webgpu::wgc::command as wgpu_com;
|
||||
use webgpu::wgc::pipeline::ProgrammableStageDescriptor;
|
||||
use webgpu::wgc::resource::TextureDescriptor;
|
||||
use webgpu::wgt::{self, AstcBlock, AstcChannel};
|
||||
|
||||
use super::bindings::codegen::Bindings::WebGPUBinding::{
|
||||
GPUProgrammableStage, GPUTextureDimension,
|
||||
GPUProgrammableStage, GPUTextureDescriptor, GPUTextureDimension,
|
||||
};
|
||||
use super::bindings::error::Error;
|
||||
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(
|
||||
bgle: &GPUBindGroupLayoutEntry,
|
||||
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 {
|
||||
type Error = Error;
|
||||
|
||||
|
|
|
@ -153,6 +153,10 @@ impl GPUDevice {
|
|||
self.device
|
||||
}
|
||||
|
||||
pub fn queue_id(&self) -> webgpu::WebGPUQueue {
|
||||
self.default_queue.id()
|
||||
}
|
||||
|
||||
pub fn channel(&self) -> WebGPU {
|
||||
self.channel.clone()
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ use webgpu::wgc::resource;
|
|||
use webgpu::{wgt, WebGPU, WebGPURequest, WebGPUTexture, WebGPUTextureView};
|
||||
|
||||
use super::bindings::error::Fallible;
|
||||
use super::gpuconvert::convert_texture_descriptor;
|
||||
use crate::dom::bindings::cell::DomRefCell;
|
||||
use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
|
||||
GPUTextureAspect, GPUTextureDescriptor, GPUTextureDimension, GPUTextureFormat,
|
||||
|
@ -127,21 +128,7 @@ impl GPUTexture {
|
|||
device: &GPUDevice,
|
||||
descriptor: &GPUTextureDescriptor,
|
||||
) -> Fallible<DomRoot<GPUTexture>> {
|
||||
let size = (&descriptor.size).try_into()?;
|
||||
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 (desc, size) = convert_texture_descriptor(descriptor, device)?;
|
||||
|
||||
let texture_id = device.global().wgpu_id_hub().create_texture_id();
|
||||
|
||||
|
|
|
@ -105,7 +105,7 @@ impl HTMLCanvasElement {
|
|||
},
|
||||
CanvasContext::WebGL(ref context) => context.recreate(size),
|
||||
CanvasContext::WebGL2(ref context) => context.recreate(size),
|
||||
CanvasContext::WebGPU(_) => unimplemented!(),
|
||||
CanvasContext::WebGPU(ref context) => context.resize(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ use base::id::PipelineId;
|
|||
use ipc_channel::ipc::{IpcSender, IpcSharedMemory};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use webrender_api::units::DeviceIntSize;
|
||||
use webrender_api::{ImageFormat, ImageKey};
|
||||
use webrender_api::ImageKey;
|
||||
use wgc::binding_model::{
|
||||
BindGroupDescriptor, BindGroupLayoutDescriptor, PipelineLayoutDescriptor,
|
||||
};
|
||||
|
@ -34,6 +34,14 @@ use crate::render_commands::RenderCommand;
|
|||
use crate::swapchain::WebGPUContextId;
|
||||
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)]
|
||||
pub enum WebGPURequest {
|
||||
BufferMapAsync {
|
||||
|
@ -103,7 +111,6 @@ pub enum WebGPURequest {
|
|||
/// present only on ASYNC versions
|
||||
async_sender: Option<IpcSender<WebGPUResponse>>,
|
||||
},
|
||||
CreateContext(IpcSender<(WebGPUContextId, ImageKey)>),
|
||||
CreatePipelineLayout {
|
||||
device_id: id::DeviceId,
|
||||
pipeline_layout_id: id::PipelineLayoutId,
|
||||
|
@ -129,14 +136,31 @@ pub enum WebGPURequest {
|
|||
label: Option<String>,
|
||||
sender: IpcSender<WebGPUResponse>,
|
||||
},
|
||||
CreateSwapChain {
|
||||
device_id: id::DeviceId,
|
||||
queue_id: id::QueueId,
|
||||
/// Creates context
|
||||
CreateContext {
|
||||
buffer_ids: ArrayVec<id::BufferId, PRESENTATION_BUFFER_COUNT>,
|
||||
context_id: WebGPUContextId,
|
||||
image_key: ImageKey,
|
||||
format: ImageFormat,
|
||||
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 {
|
||||
device_id: id::DeviceId,
|
||||
|
@ -152,10 +176,6 @@ pub enum WebGPURequest {
|
|||
DestroyBuffer(id::BufferId),
|
||||
DestroyDevice(id::DeviceId),
|
||||
DestroyTexture(id::TextureId),
|
||||
DestroySwapChain {
|
||||
context_id: WebGPUContextId,
|
||||
image_key: ImageKey,
|
||||
},
|
||||
DropTexture(id::TextureId),
|
||||
DropAdapter(id::AdapterId),
|
||||
DropDevice(id::DeviceId),
|
||||
|
@ -254,11 +274,6 @@ pub enum WebGPURequest {
|
|||
queue_id: id::QueueId,
|
||||
command_buffers: Vec<id::CommandBufferId>,
|
||||
},
|
||||
SwapChainPresent {
|
||||
context_id: WebGPUContextId,
|
||||
texture_id: id::TextureId,
|
||||
encoder_id: id::CommandEncoderId,
|
||||
},
|
||||
UnmapBuffer {
|
||||
buffer_id: id::BufferId,
|
||||
array_buffer: IpcSharedMemory,
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
use log::warn;
|
||||
use swapchain::WGPUImageMap;
|
||||
pub use swapchain::{PresentationData, WGPUExternalImages};
|
||||
pub use swapchain::{ContextData, WGPUExternalImages};
|
||||
use webrender::RenderApiSender;
|
||||
use wgpu_thread::WGPU;
|
||||
pub use {wgpu_core as wgc, wgpu_types as wgt};
|
||||
|
|
|
@ -3,21 +3,21 @@
|
|||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::ops::ControlFlow;
|
||||
use std::ptr::NonNull;
|
||||
use std::slice;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use arrayvec::ArrayVec;
|
||||
use euclid::default::Size2D;
|
||||
use ipc_channel::ipc::IpcSender;
|
||||
use log::{error, warn};
|
||||
use malloc_size_of::MallocSizeOf;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use webrender::{RenderApi, Transaction};
|
||||
use webrender_api::units::DeviceIntSize;
|
||||
use webrender_api::{
|
||||
DirtyRect, ExternalImageData, ExternalImageId, ExternalImageType, ImageData, ImageDescriptor,
|
||||
ImageDescriptorFlags, ImageFormat, ImageKey,
|
||||
DirtyRect, DocumentId, ExternalImageData, ExternalImageId, ExternalImageType, ImageData,
|
||||
ImageDescriptor, ImageDescriptorFlags, ImageFormat, ImageKey,
|
||||
};
|
||||
use webrender_traits::{WebrenderExternalImageApi, WebrenderImageSource};
|
||||
use wgpu_core::device::HostMap;
|
||||
|
@ -25,9 +25,10 @@ use wgpu_core::global::Global;
|
|||
use wgpu_core::id;
|
||||
use wgpu_core::resource::{BufferAccessError, BufferMapCallback, BufferMapOperation};
|
||||
|
||||
use crate::{wgt, WebGPUMsg};
|
||||
use crate::{wgt, ContextConfiguration, Error, WebGPUMsg};
|
||||
|
||||
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)]
|
||||
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 {
|
||||
global: Arc<Global>,
|
||||
|
@ -84,20 +85,20 @@ pub struct WGPUExternalImages {
|
|||
impl WebrenderExternalImageApi for WGPUExternalImages {
|
||||
fn lock(&mut self, id: u64) -> (WebrenderImageSource, Size2D<i32>) {
|
||||
let id = WebGPUContextId(id);
|
||||
let size;
|
||||
let data;
|
||||
if let Some(present_data) = self.images.lock().unwrap().get(&id) {
|
||||
size = present_data.image_desc.size.cast_unit();
|
||||
data = if let Some(present_data) = &present_data.data {
|
||||
present_data.slice().to_vec()
|
||||
let webgpu_contexts = self.images.lock().unwrap();
|
||||
let context_data = webgpu_contexts.get(&id).unwrap();
|
||||
let size = context_data.image_desc.size().cast_unit();
|
||||
let data = if let Some(present_buffer) = context_data
|
||||
.swap_chain
|
||||
.as_ref()
|
||||
.map(|swap_chain| swap_chain.data.as_ref())
|
||||
.flatten()
|
||||
{
|
||||
present_buffer.slice().to_vec()
|
||||
} else {
|
||||
present_data.dummy_data()
|
||||
context_data.dummy_data()
|
||||
};
|
||||
} else {
|
||||
size = Size2D::new(0, 0);
|
||||
data = Vec::new();
|
||||
}
|
||||
let _ = self.locked_ids.insert(id, data);
|
||||
self.locked_ids.insert(id, data);
|
||||
(
|
||||
WebrenderImageSource::Raw(self.locked_ids.get(&id).unwrap().as_slice()),
|
||||
size,
|
||||
|
@ -106,13 +107,13 @@ impl WebrenderExternalImageApi for WGPUExternalImages {
|
|||
|
||||
fn unlock(&mut self, id: u64) {
|
||||
let id = WebGPUContextId(id);
|
||||
let _ = self.locked_ids.remove(&id);
|
||||
self.locked_ids.remove(&id);
|
||||
}
|
||||
}
|
||||
|
||||
/// States of presentation buffer
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
|
||||
pub enum PresentationBufferState {
|
||||
enum PresentationBufferState {
|
||||
/// Initial state, buffer has yet to be created,
|
||||
/// only its id is reserved
|
||||
#[default]
|
||||
|
@ -125,61 +126,105 @@ pub enum PresentationBufferState {
|
|||
Mapped,
|
||||
}
|
||||
|
||||
pub struct PresentationData {
|
||||
struct SwapChain {
|
||||
device_id: id::DeviceId,
|
||||
queue_id: id::QueueId,
|
||||
data: Option<GPUPresentationBuffer>,
|
||||
buffer_ids: ArrayVec<(id::BufferId, PresentationBufferState), PRESENTATION_BUFFER_COUNT>,
|
||||
image_key: ImageKey,
|
||||
image_desc: ImageDescriptor,
|
||||
image_data: ImageData,
|
||||
}
|
||||
|
||||
impl PresentationData {
|
||||
pub fn new(
|
||||
device_id: id::DeviceId,
|
||||
queue_id: id::QueueId,
|
||||
buffer_ids: ArrayVec<id::BufferId, PRESENTATION_BUFFER_COUNT>,
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct WebGPUImageDescriptor(pub ImageDescriptor);
|
||||
|
||||
impl WebGPUImageDescriptor {
|
||||
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: ImageDescriptor,
|
||||
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,
|
||||
size: DeviceIntSize,
|
||||
buffer_ids: ArrayVec<id::BufferId, PRESENTATION_BUFFER_COUNT>,
|
||||
) -> Self {
|
||||
let image_data = ImageData::External(ExternalImageData {
|
||||
id: ExternalImageId(context_id.0),
|
||||
channel_index: 0,
|
||||
image_type: ExternalImageType::Buffer,
|
||||
});
|
||||
|
||||
Self {
|
||||
device_id,
|
||||
queue_id,
|
||||
data: None,
|
||||
image_key,
|
||||
image_desc: WebGPUImageDescriptor::default(size),
|
||||
image_data,
|
||||
swap_chain: None,
|
||||
buffer_ids: buffer_ids
|
||||
.iter()
|
||||
.map(|&id| (id, PresentationBufferState::Unassigned))
|
||||
.map(|&buffer_id| (buffer_id, PresentationBufferState::Unassigned))
|
||||
.collect(),
|
||||
image_key,
|
||||
image_desc,
|
||||
image_data,
|
||||
}
|
||||
}
|
||||
|
||||
fn dummy_data(&self) -> Vec<u8> {
|
||||
let size = (self
|
||||
.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
|
||||
vec![0; self.image_desc.buffer_size() as usize]
|
||||
}
|
||||
|
||||
/// Returns id of available buffer
|
||||
/// and sets state to PresentationBufferState::Mapping
|
||||
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
|
||||
.buffer_ids
|
||||
.iter_mut()
|
||||
|
@ -196,11 +241,15 @@ impl PresentationData {
|
|||
let buffer_id = *buffer_id;
|
||||
let buffer_desc = wgt::BufferDescriptor {
|
||||
label: None,
|
||||
size: self.buffer_size(),
|
||||
size: self.image_desc.buffer_size(),
|
||||
usage: wgt::BufferUsages::MAP_READ | wgt::BufferUsages::COPY_DST,
|
||||
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)
|
||||
} else {
|
||||
None
|
||||
|
@ -217,57 +266,135 @@ impl PresentationData {
|
|||
}
|
||||
|
||||
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);
|
||||
assert_eq!(*buffer_state, PresentationBufferState::Mapped);
|
||||
*buffer_state = PresentationBufferState::Available;
|
||||
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 {
|
||||
pub(crate) fn create_swapchain(
|
||||
pub(crate) fn create_context(
|
||||
&self,
|
||||
device_id: id::DeviceId,
|
||||
queue_id: id::QueueId,
|
||||
buffer_ids: ArrayVec<id::BufferId, PRESENTATION_BUFFER_COUNT>,
|
||||
context_id: WebGPUContextId,
|
||||
format: ImageFormat,
|
||||
size: DeviceIntSize,
|
||||
image_key: ImageKey,
|
||||
size: DeviceIntSize,
|
||||
buffer_ids: ArrayVec<id::BufferId, PRESENTATION_BUFFER_COUNT>,
|
||||
) {
|
||||
let image_desc = ImageDescriptor {
|
||||
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 context_data = ContextData::new(context_id, image_key, size, buffer_ids);
|
||||
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
|
||||
.lock()
|
||||
.unwrap()
|
||||
.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
|
||||
|
@ -276,39 +403,42 @@ impl crate::WGPU {
|
|||
context_id: WebGPUContextId,
|
||||
encoder_id: id::Id<id::markers::CommandEncoder>,
|
||||
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 device_id;
|
||||
let queue_id;
|
||||
let size;
|
||||
let buffer_id;
|
||||
let buffer_stride;
|
||||
let buffer_size;
|
||||
let image_desc;
|
||||
{
|
||||
if let Some(present_data) = self.wgpu_image_map.lock().unwrap().get_mut(&context_id) {
|
||||
size = present_data.image_desc.size;
|
||||
device_id = present_data.device_id;
|
||||
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(());
|
||||
if let Some(context_data) = self.wgpu_image_map.lock().unwrap().get_mut(&context_id) {
|
||||
let Some(swap_chain) = context_data.swap_chain.as_ref() else {
|
||||
return Ok(());
|
||||
};
|
||||
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 {
|
||||
error!("Data not found for {:?}", context_id);
|
||||
return ControlFlow::Break(());
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
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 {
|
||||
buffer: buffer_id,
|
||||
layout: wgt::ImageDataLayout {
|
||||
offset: 0,
|
||||
bytes_per_row: Some(buffer_stride as u32),
|
||||
bytes_per_row: Some(image_desc.buffer_stride() as u32),
|
||||
rows_per_image: None,
|
||||
},
|
||||
};
|
||||
|
@ -319,18 +449,25 @@ impl crate::WGPU {
|
|||
aspect: wgt::TextureAspect::All,
|
||||
};
|
||||
let copy_size = wgt::Extent3d {
|
||||
width: size.width as u32,
|
||||
height: size.height as u32,
|
||||
width: image_desc.size().width as u32,
|
||||
height: image_desc.size().height as u32,
|
||||
depth_or_array_layers: 1,
|
||||
};
|
||||
let _ = global.command_encoder_copy_texture_to_buffer(
|
||||
global.command_encoder_copy_texture_to_buffer(
|
||||
encoder_id,
|
||||
&texture_cv,
|
||||
&buffer_cv,
|
||||
©_size,
|
||||
);
|
||||
let _ = global.command_encoder_finish(encoder_id, &wgt::CommandBufferDescriptor::default());
|
||||
let _ = global.queue_submit(queue_id, &[encoder_id.into_command_buffer_id()]);
|
||||
)?;
|
||||
let (command_buffer_id, error) =
|
||||
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 global = Arc::clone(&self.global);
|
||||
let wgpu_image_map = Arc::clone(&self.wgpu_image_map);
|
||||
|
@ -343,11 +480,11 @@ impl crate::WGPU {
|
|||
result,
|
||||
global,
|
||||
buffer_id,
|
||||
buffer_size,
|
||||
wgpu_image_map,
|
||||
context_id,
|
||||
webrender_api,
|
||||
webrender_document,
|
||||
image_desc,
|
||||
);
|
||||
}))
|
||||
};
|
||||
|
@ -355,42 +492,23 @@ impl crate::WGPU {
|
|||
host: HostMap::Read,
|
||||
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();
|
||||
|
||||
ControlFlow::Continue(())
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn destroy_swapchain(
|
||||
&mut self,
|
||||
context_id: WebGPUContextId,
|
||||
image_key: webrender_api::ImageKey,
|
||||
) {
|
||||
let present_data = self
|
||||
.wgpu_image_map
|
||||
pub(crate) fn destroy_context(&mut self, context_id: WebGPUContextId) {
|
||||
self.wgpu_image_map
|
||||
.lock()
|
||||
.unwrap()
|
||||
.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()
|
||||
.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>,
|
||||
global: Arc<Global>,
|
||||
buffer_id: id::BufferId,
|
||||
buffer_size: u64,
|
||||
wgpu_image_map: WGPUImageMap,
|
||||
context_id: WebGPUContextId,
|
||||
webrender_api: Arc<Mutex<RenderApi>>,
|
||||
webrender_document: webrender_api::DocumentId,
|
||||
image_desc: WebGPUImageDescriptor,
|
||||
) {
|
||||
match result {
|
||||
Ok(()) => {
|
||||
if let Some(present_data) = wgpu_image_map.lock().unwrap().get_mut(&context_id) {
|
||||
let buffer_state = present_data.get_buffer_state(buffer_id);
|
||||
assert_eq!(*buffer_state, PresentationBufferState::Mapping);
|
||||
if let Some(context_data) = wgpu_image_map.lock().unwrap().get_mut(&context_id) {
|
||||
let config_changed = image_desc != context_data.image_desc;
|
||||
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;
|
||||
let presentation_buffer =
|
||||
GPUPresentationBuffer::new(global, buffer_id, buffer_size);
|
||||
let old_presentation_buffer = present_data.data.replace(presentation_buffer);
|
||||
GPUPresentationBuffer::new(global, buffer_id, image_desc.buffer_size());
|
||||
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();
|
||||
txn.update_image(
|
||||
present_data.image_key,
|
||||
present_data.image_desc,
|
||||
present_data.image_data.clone(),
|
||||
context_data.image_key,
|
||||
context_data.image_desc.0,
|
||||
context_data.image_data.clone(),
|
||||
&DirtyRect::All,
|
||||
);
|
||||
webrender_api
|
||||
|
@ -425,10 +577,10 @@ fn update_wr_image(
|
|||
.unwrap()
|
||||
.send_transaction(webrender_document, txn);
|
||||
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 {
|
||||
error!("Data not found for {:?}", context_id);
|
||||
error!("WebGPU Context {:?} is destroyed", context_id);
|
||||
}
|
||||
},
|
||||
_ => error!("Could not map buffer({:?})", buffer_id),
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::ops::ControlFlow;
|
||||
use std::slice;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
|
@ -15,7 +14,7 @@ use ipc_channel::ipc::{IpcReceiver, IpcSender, IpcSharedMemory};
|
|||
use log::{info, warn};
|
||||
use servo_config::pref;
|
||||
use webrender::{RenderApi, RenderApiSender};
|
||||
use webrender_api::DocumentId;
|
||||
use webrender_api::{DocumentId, ExternalImageId};
|
||||
use webrender_traits::{WebrenderExternalImageRegistry, WebrenderImageHandlerType};
|
||||
use wgc::command::{ComputePass, ComputePassDescriptor, RenderPass};
|
||||
use wgc::device::queue::SubmittedWorkDoneClosure;
|
||||
|
@ -404,17 +403,6 @@ impl WGPU {
|
|||
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 {
|
||||
device_id,
|
||||
pipeline_layout_id,
|
||||
|
@ -513,17 +501,79 @@ impl WGPU {
|
|||
}
|
||||
self.maybe_dispatch_wgpu_error(device_id, error);
|
||||
},
|
||||
WebGPURequest::CreateSwapChain {
|
||||
device_id,
|
||||
queue_id,
|
||||
WebGPURequest::CreateContext {
|
||||
buffer_ids,
|
||||
context_id,
|
||||
image_key,
|
||||
size,
|
||||
format,
|
||||
} => self.create_swapchain(
|
||||
device_id, queue_id, buffer_ids, context_id, format, size, image_key,
|
||||
),
|
||||
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();
|
||||
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 {
|
||||
device_id,
|
||||
texture_id,
|
||||
|
@ -561,12 +611,6 @@ impl WGPU {
|
|||
// Wake poller thread to trigger DeviceLostClosure
|
||||
self.poller.wake();
|
||||
},
|
||||
WebGPURequest::DestroySwapChain {
|
||||
context_id,
|
||||
image_key,
|
||||
} => {
|
||||
self.destroy_swapchain(context_id, image_key);
|
||||
},
|
||||
WebGPURequest::DestroyTexture(texture_id) => {
|
||||
let global = &self.global;
|
||||
let _ = global.texture_destroy(texture_id);
|
||||
|
@ -965,17 +1009,6 @@ impl WGPU {
|
|||
};
|
||||
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 {
|
||||
buffer_id,
|
||||
array_buffer,
|
||||
|
|
195
tests/wpt/webgpu/meta/webgpu/cts.https.html.ini
vendored
195
tests/wpt/webgpu/meta/webgpu/cts.https.html.ini
vendored
|
@ -9741,8 +9741,6 @@
|
|||
[:format="bc1-rgba-unorm";canvasType="onscreen";enable_required_feature=false]
|
||||
|
||||
[: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]
|
||||
|
||||
|
@ -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=true]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[: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=true]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[: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=true]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[: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=true]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[: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=true]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[: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=true]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[: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=true]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[: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=true]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[: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=true]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[: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=true]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[: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=true]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[: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=true]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[: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=true]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[: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=true]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[: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:*]
|
||||
disabled: true
|
||||
expected:
|
||||
if os == "linux" and not debug: SKIP
|
||||
[:limitTest="atDefault";testValueName="atLimit";canvasType="offscreen"]
|
||||
|
||||
[: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:*]
|
||||
|
@ -16962,8 +16968,45 @@
|
|||
|
||||
|
||||
[cts.https.html?q=webgpu:api,validation,capability_checks,limits,maxTextureDimension2D:getCurrentTexture,at_over:*]
|
||||
expected:
|
||||
if os == "linux" and not debug: CRASH
|
||||
[:limitTest="atDefault";testValueName="atLimit";canvasType="offscreen"]
|
||||
|
||||
[: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:*]
|
||||
|
@ -185733,60 +185776,32 @@
|
|||
[:canvasType="onscreen";format="astc-8x8-unorm-srgb"]
|
||||
|
||||
[:canvasType="onscreen";format="bc1-rgba-unorm"]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[:canvasType="onscreen";format="bc1-rgba-unorm-srgb"]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[:canvasType="onscreen";format="bc2-rgba-unorm"]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[:canvasType="onscreen";format="bc2-rgba-unorm-srgb"]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[:canvasType="onscreen";format="bc3-rgba-unorm"]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[:canvasType="onscreen";format="bc3-rgba-unorm-srgb"]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[:canvasType="onscreen";format="bc4-r-snorm"]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[:canvasType="onscreen";format="bc4-r-unorm"]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[:canvasType="onscreen";format="bc5-rg-snorm"]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[:canvasType="onscreen";format="bc5-rg-unorm"]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[:canvasType="onscreen";format="bc6h-rgb-float"]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[:canvasType="onscreen";format="bc6h-rgb-ufloat"]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[:canvasType="onscreen";format="bc7-rgba-unorm"]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[:canvasType="onscreen";format="bc7-rgba-unorm-srgb"]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[:canvasType="onscreen";format="bgra8unorm"]
|
||||
|
||||
|
@ -185863,8 +185878,6 @@
|
|||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[:canvasType="onscreen";format="r8snorm"]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[:canvasType="onscreen";format="r8uint"]
|
||||
expected:
|
||||
|
@ -185875,8 +185888,6 @@
|
|||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[:canvasType="onscreen";format="rg11b10ufloat"]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[:canvasType="onscreen";format="rg16float"]
|
||||
expected:
|
||||
|
@ -185907,8 +185918,6 @@
|
|||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[:canvasType="onscreen";format="rg8snorm"]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[:canvasType="onscreen";format="rg8uint"]
|
||||
expected:
|
||||
|
@ -185927,8 +185936,6 @@
|
|||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[:canvasType="onscreen";format="rgb9e5ufloat"]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[:canvasType="onscreen";format="rgba16float"]
|
||||
expected:
|
||||
|
@ -185959,8 +185966,6 @@
|
|||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[:canvasType="onscreen";format="rgba8snorm"]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[:canvasType="onscreen";format="rgba8uint"]
|
||||
expected:
|
||||
|
@ -185978,13 +185983,23 @@
|
|||
|
||||
|
||||
[cts.https.html?q=webgpu:web_platform,canvas,configure:size_zero_after_configure:*]
|
||||
expected:
|
||||
if os == "linux" and not debug: CRASH
|
||||
[:canvasType="offscreen";zeroDimension="height"]
|
||||
|
||||
[: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:*]
|
||||
expected:
|
||||
if os == "linux" and not debug: CRASH
|
||||
[:canvasType="offscreen";zeroDimension="height"]
|
||||
|
||||
[:canvasType="offscreen";zeroDimension="width"]
|
||||
|
||||
[:canvasType="onscreen";zeroDimension="height"]
|
||||
|
||||
[:canvasType="onscreen";zeroDimension="width"]
|
||||
|
||||
|
||||
[cts.https.html?q=webgpu:web_platform,canvas,configure:usage:*]
|
||||
|
@ -186027,18 +186042,12 @@
|
|||
[:canvasType="offscreen";format="rgba8unorm";viewFormatFeature="texture-compression-etc2"]
|
||||
|
||||
[:canvasType="onscreen";format="bgra8unorm";viewFormatFeature="_undef_"]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[: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-bc"]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[:canvasType="onscreen";format="bgra8unorm";viewFormatFeature="texture-compression-etc2"]
|
||||
|
||||
|
@ -186047,30 +186056,20 @@
|
|||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[: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-bc"]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[:canvasType="onscreen";format="rgba16float";viewFormatFeature="texture-compression-etc2"]
|
||||
|
||||
[:canvasType="onscreen";format="rgba8unorm";viewFormatFeature="_undef_"]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[: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-bc"]
|
||||
expected:
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
||||
[:canvasType="onscreen";format="rgba8unorm";viewFormatFeature="texture-compression-etc2"]
|
||||
|
||||
|
@ -186082,8 +186081,9 @@
|
|||
|
||||
|
||||
[cts.https.html?q=webgpu:web_platform,canvas,getCurrentTexture:configured:*]
|
||||
expected:
|
||||
if os == "linux" and not debug: CRASH
|
||||
[:canvasType="offscreen"]
|
||||
|
||||
[:canvasType="onscreen"]
|
||||
|
||||
|
||||
[cts.https.html?q=webgpu:web_platform,canvas,getCurrentTexture:expiry:*]
|
||||
|
@ -186119,8 +186119,9 @@
|
|||
|
||||
|
||||
[cts.https.html?q=webgpu:web_platform,canvas,getCurrentTexture:resize:*]
|
||||
expected:
|
||||
if os == "linux" and not debug: CRASH
|
||||
[:canvasType="offscreen"]
|
||||
|
||||
[:canvasType="onscreen"]
|
||||
|
||||
|
||||
[cts.https.html?q=webgpu:web_platform,canvas,getCurrentTexture:single_frames:*]
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
[canvas_clear.https.html]
|
||||
expected:
|
||||
if os == "win": CRASH
|
||||
if os == "linux" and debug: CRASH
|
||||
if os == "linux" and not debug: TIMEOUT
|
||||
if os == "mac": CRASH
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
[canvas_colorspace_rgba16float.https.html]
|
||||
expected:
|
||||
if os == "win": CRASH
|
||||
if os == "linux" and debug: CRASH
|
||||
if os == "linux" and not debug: TIMEOUT
|
||||
if os == "mac": CRASH
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
[canvas_complex_rgba16float_copy.https.html]
|
||||
expected:
|
||||
if os == "win": CRASH
|
||||
if os == "linux" and debug: CRASH
|
||||
if os == "linux" and not debug: TIMEOUT
|
||||
if os == "mac": CRASH
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
[canvas_complex_rgba16float_draw.https.html]
|
||||
expected:
|
||||
if os == "win": CRASH
|
||||
if os == "linux" and debug: CRASH
|
||||
if os == "linux" and not debug: TIMEOUT
|
||||
if os == "mac": CRASH
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
[canvas_complex_rgba16float_store.https.html]
|
||||
expected:
|
||||
if os == "win": CRASH
|
||||
if os == "linux" and debug: CRASH
|
||||
if os == "linux" and not debug: TIMEOUT
|
||||
if os == "mac": CRASH
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
[canvas_complex_rgba8unorm_store.https.html]
|
||||
expected:
|
||||
if os == "linux" and not debug: [PASS, FAIL]
|
||||
if os == "linux" and not debug: PASS
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
[canvas_composite_alpha_bgra8unorm_opaque_copy.https.html]
|
||||
expected:
|
||||
if os == "linux" and not debug: [CRASH, PASS, FAIL]
|
||||
if os == "linux" and not debug: PASS
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
[canvas_composite_alpha_bgra8unorm_opaque_draw.https.html]
|
||||
expected: [CRASH, PASS, FAIL]
|
||||
expected: PASS
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
[canvas_composite_alpha_bgra8unorm_premultiplied_copy.https.html]
|
||||
expected: FAIL
|
||||
expected:
|
||||
if os == "linux" and not debug: PASS
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
[canvas_composite_alpha_bgra8unorm_premultiplied_draw.https.html]
|
||||
expected: FAIL
|
||||
expected:
|
||||
if os == "linux" and not debug: PASS
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
[canvas_composite_alpha_rgba16float_opaque_copy.https.html]
|
||||
expected:
|
||||
if os == "win": CRASH
|
||||
if os == "linux" and debug: CRASH
|
||||
if os == "linux" and not debug: TIMEOUT
|
||||
if os == "mac": CRASH
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
[canvas_composite_alpha_rgba16float_opaque_draw.https.html]
|
||||
expected:
|
||||
if os == "win": CRASH
|
||||
if os == "linux" and debug: CRASH
|
||||
if os == "linux" and not debug: TIMEOUT
|
||||
if os == "mac": CRASH
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
[canvas_composite_alpha_rgba16float_premultiplied_copy.https.html]
|
||||
expected:
|
||||
if os == "win": CRASH
|
||||
if os == "linux" and debug: CRASH
|
||||
if os == "linux" and not debug: TIMEOUT
|
||||
if os == "mac": CRASH
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
[canvas_composite_alpha_rgba16float_premultiplied_draw.https.html]
|
||||
expected:
|
||||
if os == "win": CRASH
|
||||
if os == "linux" and debug: CRASH
|
||||
if os == "linux" and not debug: TIMEOUT
|
||||
if os == "mac": CRASH
|
||||
if os == "linux" and not debug: FAIL
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
[canvas_composite_alpha_rgba8unorm_opaque_copy.https.html]
|
||||
expected:
|
||||
if os == "linux" and not debug: [CRASH, PASS, FAIL]
|
||||
if os == "linux" and not debug: PASS
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
[canvas_composite_alpha_rgba8unorm_opaque_draw.https.html]
|
||||
expected:
|
||||
if os == "linux" and not debug: [PASS, FAIL]
|
||||
if os == "linux" and not debug: PASS
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
[canvas_composite_alpha_rgba8unorm_premultiplied_copy.https.html]
|
||||
expected: FAIL
|
||||
expected:
|
||||
if os == "linux" and not debug: PASS
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
[canvas_composite_alpha_rgba8unorm_premultiplied_draw.https.html]
|
||||
expected: [CRASH, FAIL]
|
||||
expected:
|
||||
if os == "linux" and not debug: PASS
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
[delay_get_texture.https.html]
|
||||
expected:
|
||||
if os == "win": FAIL
|
||||
if os == "linux" and debug: FAIL
|
||||
if os == "linux" and not debug: [PASS, FAIL]
|
||||
if os == "mac": FAIL
|
||||
if os == "linux" and not debug: PASS
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue