mirror of
https://github.com/servo/servo.git
synced 2025-09-30 00:29:14 +01:00
webgpu: Simplify presentation and handle cleared in script (#38717)
There are many important changes here: - Generalize the presentation buffer into standalone staging buffers that hold their own state. This allow them to be used by getImage. - Move all clear handling to the ScriptThread and send the configuration on each request present/getimage, thus avoiding any recreate/clearing messages. This means that we prepare staging buffers lazily, on the first request. Try run for this change: https://github.com/sagudev/servo/actions/runs/17341982368 Testing: This is covered by existing WebGPU CTS tests. There are some bad expectations updates, but they are also on main (presumably from last update the rendering work) although I think CTS is actually wrong (see https://github.com/gpuweb/cts/issues/4440). Fixes: #36820 Fixes: #37705 Fixes: #33368 (we now keep reference alive in hashmap) --------- Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
This commit is contained in:
parent
8d2723b2c9
commit
1f0f079203
29 changed files with 1023 additions and 935 deletions
|
@ -3,19 +3,20 @@
|
|||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::cell::{Cell, RefCell};
|
||||
|
||||
use arrayvec::ArrayVec;
|
||||
use base::Epoch;
|
||||
use dom_struct::dom_struct;
|
||||
use ipc_channel::ipc::{self};
|
||||
use pixels::Snapshot;
|
||||
use script_bindings::codegen::GenericBindings::WebGPUBinding::GPUTextureFormat;
|
||||
use script_bindings::inheritance::Castable;
|
||||
use webgpu_traits::{
|
||||
ContextConfiguration, PRESENTATION_BUFFER_COUNT, WebGPU, WebGPUContextId, WebGPURequest,
|
||||
WebGPUTexture,
|
||||
ContextConfiguration, PRESENTATION_BUFFER_COUNT, PendingTexture, WebGPU, WebGPUContextId,
|
||||
WebGPURequest,
|
||||
};
|
||||
use webrender_api::ImageKey;
|
||||
use webrender_api::units::DeviceIntSize;
|
||||
use webrender_api::{ImageFormat, ImageKey};
|
||||
use wgpu_core::id;
|
||||
|
||||
use super::gpuconvert::convert_texture_descriptor;
|
||||
|
@ -24,24 +25,20 @@ use crate::canvas_context::{
|
|||
CanvasContext, CanvasHelpers, HTMLCanvasElementOrOffscreenCanvas,
|
||||
LayoutCanvasRenderingContextHelpers,
|
||||
};
|
||||
use crate::conversions::Convert;
|
||||
use crate::dom::bindings::codegen::Bindings::GPUCanvasContextBinding::GPUCanvasContextMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::WebGPUBinding::GPUTexture_Binding::GPUTextureMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
|
||||
GPUCanvasAlphaMode, GPUCanvasConfiguration, GPUDeviceMethods, GPUExtent3D, GPUExtent3DDict,
|
||||
GPUObjectDescriptorBase, GPUTextureDescriptor, GPUTextureDimension, GPUTextureFormat,
|
||||
GPUTextureUsageConstants,
|
||||
GPUObjectDescriptorBase, GPUTextureDescriptor, GPUTextureDimension, GPUTextureUsageConstants,
|
||||
};
|
||||
use crate::dom::bindings::codegen::UnionTypes::HTMLCanvasElementOrOffscreenCanvas as RootedHTMLCanvasElementOrOffscreenCanvas;
|
||||
use crate::dom::bindings::error::{Error, Fallible};
|
||||
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
|
||||
use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom};
|
||||
use crate::dom::bindings::str::USVString;
|
||||
use crate::dom::bindings::weakref::WeakRef;
|
||||
use crate::dom::document::WebGPUContextsMap;
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
use crate::dom::html::htmlcanvaselement::HTMLCanvasElement;
|
||||
use crate::dom::node::NodeTraits;
|
||||
use crate::dom::node::{Node, NodeDamage, NodeTraits};
|
||||
use crate::script_runtime::CanGc;
|
||||
|
||||
/// <https://gpuweb.github.io/gpuweb/#supported-context-formats>
|
||||
|
@ -53,18 +50,6 @@ fn supported_context_format(format: GPUTextureFormat) -> bool {
|
|||
)
|
||||
}
|
||||
|
||||
#[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(crate) struct GPUCanvasContext {
|
||||
reflector_: Reflector,
|
||||
|
@ -73,7 +58,6 @@ pub(crate) struct GPUCanvasContext {
|
|||
channel: WebGPU,
|
||||
/// <https://gpuweb.github.io/gpuweb/#dom-gpucanvascontext-canvas>
|
||||
canvas: HTMLCanvasElementOrOffscreenCanvas,
|
||||
// TODO: can we have wgpu surface that is hw accelerated inside wr ...
|
||||
#[ignore_malloc_size_of = "Defined in webrender"]
|
||||
#[no_trace]
|
||||
webrender_image: ImageKey,
|
||||
|
@ -84,13 +68,11 @@ pub(crate) struct GPUCanvasContext {
|
|||
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>
|
||||
current_texture: MutNullableDom<GPUTexture>,
|
||||
/// This is used for clearing
|
||||
#[ignore_malloc_size_of = "Rc are hard"]
|
||||
webgpu_contexts: WebGPUContextsMap,
|
||||
/// Set if image is cleared
|
||||
/// (usually done by [`GPUCanvasContext::replace_drawing_buffer`])
|
||||
cleared: Cell<bool>,
|
||||
}
|
||||
|
||||
impl GPUCanvasContext {
|
||||
|
@ -99,7 +81,6 @@ impl GPUCanvasContext {
|
|||
global: &GlobalScope,
|
||||
canvas: HTMLCanvasElementOrOffscreenCanvas,
|
||||
channel: WebGPU,
|
||||
webgpu_contexts: WebGPUContextsMap,
|
||||
) -> Self {
|
||||
let (sender, receiver) = ipc::channel().unwrap();
|
||||
let size = canvas.size().cast().cast_unit();
|
||||
|
@ -121,15 +102,10 @@ impl GPUCanvasContext {
|
|||
canvas,
|
||||
webrender_image,
|
||||
context_id: WebGPUContextId(external_id.0),
|
||||
drawing_buffer: RefCell::new(DrawingBuffer {
|
||||
size,
|
||||
cleared: true,
|
||||
..Default::default()
|
||||
}),
|
||||
configuration: RefCell::new(None),
|
||||
texture_descriptor: RefCell::new(None),
|
||||
current_texture: MutNullableDom::default(),
|
||||
webgpu_contexts,
|
||||
cleared: Cell::new(true),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -139,45 +115,38 @@ impl GPUCanvasContext {
|
|||
channel: WebGPU,
|
||||
can_gc: CanGc,
|
||||
) -> DomRoot<Self> {
|
||||
let document = canvas.owner_document();
|
||||
let this = reflect_dom_object(
|
||||
reflect_dom_object(
|
||||
Box::new(GPUCanvasContext::new_inherited(
|
||||
global,
|
||||
HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(Dom::from_ref(canvas)),
|
||||
channel,
|
||||
document.webgpu_contexts(),
|
||||
)),
|
||||
global,
|
||||
can_gc,
|
||||
);
|
||||
this.webgpu_contexts
|
||||
.borrow_mut()
|
||||
.entry(this.context_id())
|
||||
.or_insert_with(|| WeakRef::new(&this));
|
||||
this
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Abstract ops from spec
|
||||
impl GPUCanvasContext {
|
||||
/// <https://gpuweb.github.io/gpuweb/#abstract-opdef-gputexturedescriptor-for-the-canvas-and-configuration>
|
||||
fn texture_descriptor_for_canvas(
|
||||
fn texture_descriptor_for_canvas_and_configuration(
|
||||
&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,
|
||||
height: size.height,
|
||||
depthOrArrayLayers: 1,
|
||||
}),
|
||||
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,
|
||||
viewFormats: configuration.viewFormats.clone(),
|
||||
// other members to default
|
||||
// All other members set to their defaults.
|
||||
mipLevelCount: 1,
|
||||
sampleCount: 1,
|
||||
parent: GPUObjectDescriptorBase {
|
||||
|
@ -188,89 +157,65 @@ impl GPUCanvasContext {
|
|||
}
|
||||
|
||||
/// <https://gpuweb.github.io/gpuweb/#abstract-opdef-expire-the-current-texture>
|
||||
fn expire_current_texture(&self, canvas_epoch: Option<Epoch>) -> bool {
|
||||
// Step 1: If context.[[currentTexture]] is not null:
|
||||
let Some(current_texture) = self.current_texture.take() else {
|
||||
return false;
|
||||
};
|
||||
fn expire_current_texture(&self, skip_dirty: bool) {
|
||||
// 1. If context.[[currentTexture]] is not null:
|
||||
|
||||
// Make copy of texture content
|
||||
let did_swap = self.send_swap_chain_present(current_texture.id(), canvas_epoch);
|
||||
if let Some(current_texture) = self.current_texture.take() {
|
||||
// 1.2 Set context.[[currentTexture]] to null.
|
||||
|
||||
// Step 1.1: Call context.currentTexture.destroy() (without destroying
|
||||
// context.drawingBuffer) to terminate write access to the image.
|
||||
current_texture.Destroy();
|
||||
|
||||
// Step 1.2: Set context.[[currentTexture]] to null.
|
||||
// This is handled by the call to `.take()` above.
|
||||
|
||||
did_swap
|
||||
// 1.1 Call context.[[currentTexture]].destroy()
|
||||
// (without destroying context.[[drawingBuffer]])
|
||||
// to terminate write access to the image.
|
||||
current_texture.Destroy()
|
||||
// we can safely destroy content here,
|
||||
// because we already copied content when doing present
|
||||
// or current texture is getting cleared
|
||||
}
|
||||
// We skip marking the canvas as dirty again if we are already
|
||||
// in the process of updating the rendering.
|
||||
if !skip_dirty {
|
||||
// texture is either cleared or applied to canvas
|
||||
self.mark_as_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://gpuweb.github.io/gpuweb/#abstract-opdef-replace-the-drawing-buffer>
|
||||
fn replace_drawing_buffer(&self) {
|
||||
// Step 1
|
||||
self.expire_current_texture(None);
|
||||
// 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.convert(),
|
||||
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,
|
||||
})
|
||||
.expect("Failed to update webgpu context");
|
||||
// 1. Expire the current texture of context.
|
||||
self.expire_current_texture(false);
|
||||
// 2. Let configuration be context.[[configuration]].
|
||||
// 3. Set context.[[drawingBuffer]] to
|
||||
// a transparent black image of the same size as context.canvas
|
||||
self.cleared.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Internal helper methods
|
||||
impl GPUCanvasContext {
|
||||
fn layout_handle(&self) -> Option<ImageKey> {
|
||||
if self.drawing_buffer.borrow().cleared {
|
||||
None
|
||||
} else {
|
||||
Some(self.webrender_image)
|
||||
}
|
||||
fn context_configuration(&self) -> Option<ContextConfiguration> {
|
||||
let configuration = self.configuration.borrow();
|
||||
let configuration = configuration.as_ref()?;
|
||||
Some(ContextConfiguration {
|
||||
device_id: configuration.device.id().0,
|
||||
queue_id: configuration.device.queue_id().0,
|
||||
format: match configuration.format {
|
||||
GPUTextureFormat::Bgra8unorm => ImageFormat::BGRA8,
|
||||
GPUTextureFormat::Rgba8unorm => ImageFormat::RGBA8,
|
||||
_ => unreachable!("Configure method should set valid texture format"),
|
||||
},
|
||||
is_opaque: matches!(configuration.alphaMode, GPUCanvasAlphaMode::Opaque),
|
||||
size: self.size(),
|
||||
})
|
||||
}
|
||||
|
||||
fn send_swap_chain_present(
|
||||
&self,
|
||||
texture_id: WebGPUTexture,
|
||||
canvas_epoch: Option<Epoch>,
|
||||
) -> bool {
|
||||
self.drawing_buffer.borrow_mut().cleared = false;
|
||||
let encoder_id = self.global().wgpu_id_hub().create_command_encoder_id();
|
||||
let send_result = self.channel.0.send(WebGPURequest::SwapChainPresent {
|
||||
context_id: self.context_id,
|
||||
texture_id: texture_id.0,
|
||||
encoder_id,
|
||||
canvas_epoch,
|
||||
});
|
||||
|
||||
if let Err(error) = &send_result {
|
||||
warn!(
|
||||
"Failed to send UpdateWebrenderData({:?}) ({error})",
|
||||
self.context_id,
|
||||
);
|
||||
}
|
||||
|
||||
send_result.is_ok()
|
||||
fn pending_texture(&self) -> Option<PendingTexture> {
|
||||
self.current_texture.get().map(|texture| PendingTexture {
|
||||
texture_id: texture.id().0,
|
||||
encoder_id: self.global().wgpu_id_hub().create_command_encoder_id(),
|
||||
configuration: self
|
||||
.context_configuration()
|
||||
.expect("Context should be configured if there is a texture."),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -281,32 +226,45 @@ impl CanvasContext for GPUCanvasContext {
|
|||
self.context_id
|
||||
}
|
||||
|
||||
/// <https://gpuweb.github.io/gpuweb/#abstract-opdef-updating-the-rendering-of-a-webgpu-canvas>
|
||||
fn update_rendering(&self, canvas_epoch: Epoch) -> bool {
|
||||
if !self.onscreen() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 1: Expire the current texture of context.
|
||||
self.expire_current_texture(Some(canvas_epoch))
|
||||
// Step 2: Set context.[[lastPresentedImage]] to context.[[drawingBuffer]].
|
||||
// TODO: Implement this.
|
||||
}
|
||||
|
||||
fn image_key(&self) -> Option<ImageKey> {
|
||||
Some(self.webrender_image)
|
||||
}
|
||||
|
||||
/// <https://gpuweb.github.io/gpuweb/#abstract-opdef-updating-the-rendering-of-a-webgpu-canvas>
|
||||
fn update_rendering(&self, canvas_epoch: Epoch) -> bool {
|
||||
// Present by updating the image in WebRender. This will copy the texture into
|
||||
// the presentation buffer and use it for presenting or send a cleared image to WebRender.
|
||||
if let Err(error) = self.channel.0.send(WebGPURequest::Present {
|
||||
context_id: self.context_id,
|
||||
pending_texture: self.pending_texture(),
|
||||
size: self.size(),
|
||||
canvas_epoch,
|
||||
}) {
|
||||
warn!(
|
||||
"Failed to send WebGPURequest::Present({:?}) ({error})",
|
||||
self.context_id
|
||||
);
|
||||
}
|
||||
|
||||
// 1. Expire the current texture of context.
|
||||
self.expire_current_texture(true);
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// <https://gpuweb.github.io/gpuweb/#abstract-opdef-update-the-canvas-size>
|
||||
fn resize(&self) {
|
||||
// Step 1
|
||||
// 1. Replace the drawing buffer of context.
|
||||
self.replace_drawing_buffer();
|
||||
// Step 2
|
||||
// 2. Let configuration be context.[[configuration]]
|
||||
let configuration = self.configuration.borrow();
|
||||
// Step 3
|
||||
// 3. If configuration is not null:
|
||||
if let Some(configuration) = configuration.as_ref() {
|
||||
self.texture_descriptor
|
||||
.replace(Some(self.texture_descriptor_for_canvas(configuration)));
|
||||
// 3.1. Set context.[[textureDescriptor]] to the
|
||||
// GPUTextureDescriptor for the canvas and configuration(canvas, configuration).
|
||||
self.texture_descriptor.replace(Some(
|
||||
self.texture_descriptor_for_canvas_and_configuration(configuration),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -317,7 +275,7 @@ impl CanvasContext for GPUCanvasContext {
|
|||
/// <https://gpuweb.github.io/gpuweb/#ref-for-abstract-opdef-get-a-copy-of-the-image-contents-of-a-context%E2%91%A5>
|
||||
fn get_image_data(&self) -> Option<Snapshot> {
|
||||
// 1. Return a copy of the image contents of context.
|
||||
Some(if self.drawing_buffer.borrow().cleared {
|
||||
Some(if self.cleared.get() {
|
||||
Snapshot::cleared(self.size())
|
||||
} else {
|
||||
let (sender, receiver) = ipc::channel().unwrap();
|
||||
|
@ -325,21 +283,30 @@ impl CanvasContext for GPUCanvasContext {
|
|||
.0
|
||||
.send(WebGPURequest::GetImage {
|
||||
context_id: self.context_id,
|
||||
// We need to read from the pending texture, if one exists.
|
||||
pending_texture: self.pending_texture(),
|
||||
sender,
|
||||
})
|
||||
.unwrap();
|
||||
receiver.recv().unwrap().to_owned()
|
||||
.ok()?;
|
||||
receiver.recv().ok()?.to_owned()
|
||||
})
|
||||
}
|
||||
|
||||
fn canvas(&self) -> Option<RootedHTMLCanvasElementOrOffscreenCanvas> {
|
||||
Some(RootedHTMLCanvasElementOrOffscreenCanvas::from(&self.canvas))
|
||||
}
|
||||
|
||||
fn mark_as_dirty(&self) {
|
||||
if let HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(ref canvas) = self.canvas {
|
||||
canvas.upcast::<Node>().dirty(NodeDamage::Other);
|
||||
canvas.owner_document().add_dirty_webgpu_context(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LayoutCanvasRenderingContextHelpers for LayoutDom<'_, GPUCanvasContext> {
|
||||
fn canvas_data_source(self) -> Option<ImageKey> {
|
||||
(*self.unsafe_get()).layout_handle()
|
||||
(*self.unsafe_get()).image_key()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -351,19 +318,20 @@ impl GPUCanvasContextMethods<crate::DomTypeHolder> for GPUCanvasContext {
|
|||
|
||||
/// <https://gpuweb.github.io/gpuweb/#dom-gpucanvascontext-configure>
|
||||
fn Configure(&self, configuration: &GPUCanvasConfiguration) -> Fallible<()> {
|
||||
// Step 1: Let device be configuration.device
|
||||
// 1. Let device be configuration.device
|
||||
let device = &configuration.device;
|
||||
|
||||
// Step 5: Let descriptor be the GPUTextureDescriptor for the canvas and configuration.
|
||||
let descriptor = self.texture_descriptor_for_canvas(configuration);
|
||||
// 5. Let descriptor be the GPUTextureDescriptor for the canvas and configuration.
|
||||
let descriptor = self.texture_descriptor_for_canvas_and_configuration(configuration);
|
||||
|
||||
// Step 2&3: Validate texture format required features
|
||||
let (mut desc, _) = convert_texture_descriptor(&descriptor, device)?;
|
||||
desc.label = Some(Cow::Borrowed(
|
||||
// 2. Validate texture format required features of configuration.format with device.[[device]].
|
||||
// 3. Validate texture format required features of each element of configuration.viewFormats with device.[[device]].
|
||||
let (mut wgpu_descriptor, _) = convert_texture_descriptor(&descriptor, device)?;
|
||||
wgpu_descriptor.label = Some(Cow::Borrowed(
|
||||
"dummy texture for texture descriptor validation",
|
||||
));
|
||||
|
||||
// Step 4: If Supported context formats does not contain configuration.format, throw a TypeError
|
||||
// 4. If Supported context formats does not contain configuration.format, throw a TypeError
|
||||
if !supported_context_format(configuration.format) {
|
||||
return Err(Error::Type(format!(
|
||||
"Unsupported context format: {:?}",
|
||||
|
@ -371,23 +339,23 @@ impl GPUCanvasContextMethods<crate::DomTypeHolder> for GPUCanvasContext {
|
|||
)));
|
||||
}
|
||||
|
||||
// Step 5
|
||||
// 6. Let this.[[configuration]] to configuration.
|
||||
self.configuration.replace(Some(configuration.clone()));
|
||||
|
||||
// Step 6
|
||||
// 7. Set this.[[textureDescriptor]] to descriptor.
|
||||
self.texture_descriptor.replace(Some(descriptor));
|
||||
|
||||
// Step 7
|
||||
// 8. Replace the drawing buffer of this.
|
||||
self.replace_drawing_buffer();
|
||||
|
||||
// Step 8: Validate texture descriptor
|
||||
// 9. Validate texture descriptor
|
||||
let texture_id = self.global().wgpu_id_hub().create_texture_id();
|
||||
self.channel
|
||||
.0
|
||||
.send(WebGPURequest::ValidateTextureDescriptor {
|
||||
device_id: device.id().0,
|
||||
texture_id,
|
||||
descriptor: desc,
|
||||
descriptor: wgpu_descriptor,
|
||||
})
|
||||
.expect("Failed to create WebGPU SwapChain");
|
||||
|
||||
|
@ -396,45 +364,49 @@ impl GPUCanvasContextMethods<crate::DomTypeHolder> for GPUCanvasContext {
|
|||
|
||||
/// <https://gpuweb.github.io/gpuweb/#dom-gpucanvascontext-unconfigure>
|
||||
fn Unconfigure(&self) {
|
||||
// Step 1
|
||||
// 1. Set this.[[configuration]] to null.
|
||||
self.configuration.take();
|
||||
// Step 2
|
||||
// 2. Set this.[[textureDescriptor]] to null.
|
||||
self.current_texture.take();
|
||||
// Step 3
|
||||
// 3. Replace the drawing buffer of this.
|
||||
self.replace_drawing_buffer();
|
||||
}
|
||||
|
||||
/// <https://gpuweb.github.io/gpuweb/#dom-gpucanvascontext-getcurrenttexture>
|
||||
fn GetCurrentTexture(&self) -> Fallible<DomRoot<GPUTexture>> {
|
||||
// Step 1: If this.[[configuration]] is null, throw an InvalidStateError and return.
|
||||
// 1. If this.[[configuration]] is null, throw an InvalidStateError and return.
|
||||
let configuration = self.configuration.borrow();
|
||||
let Some(configuration) = configuration.as_ref() else {
|
||||
return Err(Error::InvalidState);
|
||||
};
|
||||
// Step 2: Assert this.[[textureDescriptor]] is not null.
|
||||
// 2. Assert this.[[textureDescriptor]] is not null.
|
||||
let texture_descriptor = self.texture_descriptor.borrow();
|
||||
let texture_descriptor = texture_descriptor.as_ref().unwrap();
|
||||
// Step 6
|
||||
// 3. Let device be this.[[configuration]].device.
|
||||
let device = &configuration.device;
|
||||
let current_texture = if let Some(current_texture) = self.current_texture.get() {
|
||||
current_texture
|
||||
} else {
|
||||
// Step 4.1
|
||||
// If this.[[currentTexture]] is null:
|
||||
// 4.1. Replace the drawing buffer of this.
|
||||
self.replace_drawing_buffer();
|
||||
// Step 4.2
|
||||
let current_texture = configuration.device.CreateTexture(texture_descriptor)?;
|
||||
// 4.2. Set this.[[currentTexture]] to the result of calling device.createTexture() with this.[[textureDescriptor]],
|
||||
// except with the GPUTexture’s underlying storage pointing to this.[[drawingBuffer]].
|
||||
let current_texture = device.CreateTexture(texture_descriptor)?;
|
||||
self.current_texture.set(Some(¤t_texture));
|
||||
// We only need to mark new texture
|
||||
self.mark_as_dirty();
|
||||
|
||||
// The content of the texture is the content of the canvas.
|
||||
self.cleared.set(false);
|
||||
|
||||
current_texture
|
||||
};
|
||||
// Step 6
|
||||
// 6. Return this.[[currentTexture]].
|
||||
Ok(current_texture)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for GPUCanvasContext {
|
||||
fn drop(&mut self) {
|
||||
self.webgpu_contexts.borrow_mut().remove(&self.context_id());
|
||||
if let Err(e) = self.channel.0.send(WebGPURequest::DestroyContext {
|
||||
context_id: self.context_id,
|
||||
}) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue