Skip to content

Commit 40e78df

Browse files
[deno] Associate external memory with each command encoder on Metal (#8704)
* [deno] Associate external memory with each command encoder on Metal This encourages doing garbage collection before the command buffer limit is reached. * Add bug reference
1 parent 39f6596 commit 40e78df

File tree

3 files changed

+57
-7
lines changed

3 files changed

+57
-7
lines changed

cts_runner/test.lst

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,12 @@ webgpu:api,validation,resource_usages,texture,in_pass_encoder:shader_stages_and_
140140
webgpu:api,validation,resource_usages,texture,in_pass_encoder:subresources_and_binding_types_combination_for_aspect:*
141141
webgpu:api,validation,resource_usages,texture,in_pass_encoder:subresources_and_binding_types_combination_for_color:compute=false;type0="render-target";type1="render-target"
142142
webgpu:api,validation,resource_usages,texture,in_pass_encoder:unused_bindings_in_pipeline:*
143-
webgpu:api,validation,resource_usages,texture,in_render_common:subresources,multiple_bind_groups:bg0Levels={"base":0,"count":1};*
143+
webgpu:api,validation,resource_usages,texture,in_render_common:subresources,color_attachments:*
144+
webgpu:api,validation,resource_usages,texture,in_render_common:subresources,color_attachment_and_bind_group:*
145+
// FAIL: webgpu:api,validation,resource_usages,texture,in_render_common:subresources,depth_stencil_attachment_and_bind_group:*
146+
// https://github.com/gfx-rs/wgpu/issues/8705
147+
webgpu:api,validation,resource_usages,texture,in_render_common:subresources,multiple_bind_groups:*
148+
webgpu:api,validation,resource_usages,texture,in_render_common:subresources,depth_stencil_texture_in_bind_groups:*
144149
webgpu:api,validation,texture,rg11b10ufloat_renderable:*
145150
webgpu:api,operation,render_pipeline,overrides:*
146151
webgpu:api,operation,rendering,3d_texture_slices:*

deno_webgpu/command_encoder.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
use std::borrow::Cow;
44
use std::cell::RefCell;
55
use std::num::NonZero;
6+
#[cfg(target_vendor = "apple")]
7+
use std::sync::OnceLock;
68

79
use deno_core::cppgc::Ptr;
810
use deno_core::op2;
@@ -30,6 +32,11 @@ pub struct GPUCommandEncoder {
3032

3133
pub id: wgpu_core::id::CommandEncoderId,
3234
pub label: String,
35+
36+
// Weak reference to the JS object so we can attach a finalizer.
37+
// See `GPUDevice::create_command_encoder`.
38+
#[cfg(target_vendor = "apple")]
39+
pub(crate) weak: OnceLock<v8::Weak<v8::Object>>,
3340
}
3441

3542
impl Drop for GPUCommandEncoder {

deno_webgpu/device.rs

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ use std::cell::RefCell;
55
use std::num::NonZeroU64;
66
use std::rc::Rc;
77

8-
use deno_core::cppgc::make_cppgc_object;
9-
use deno_core::cppgc::SameObject;
8+
use deno_core::cppgc::{make_cppgc_object, SameObject};
109
use deno_core::op2;
1110
use deno_core::v8;
1211
use deno_core::webidl::WebIdlInterfaceConverter;
@@ -531,18 +530,30 @@ impl GPUDevice {
531530
self.new_render_pipeline(descriptor)
532531
}
533532

534-
#[cppgc]
535-
fn create_command_encoder(
533+
fn create_command_encoder<'a>(
536534
&self,
535+
scope: &mut v8::HandleScope<'a>,
537536
#[webidl] descriptor: Option<
538537
super::command_encoder::GPUCommandEncoderDescriptor,
539538
>,
540-
) -> GPUCommandEncoder {
539+
) -> v8::Local<'a, v8::Object> {
540+
// Metal imposes a limit on the number of outstanding command buffers.
541+
// Attempting to create another command buffer after reaching that limit
542+
// will block, which can result in a deadlock if GC is required to
543+
// recover old command buffers. To encourage V8 to garbage collect
544+
// command buffers before that happens, we associate some external
545+
// memory with each command buffer.
546+
#[cfg(target_vendor = "apple")]
547+
const EXTERNAL_MEMORY_AMOUNT: i64 = 1 << 16;
548+
541549
let label = descriptor.map(|d| d.label).unwrap_or_default();
542550
let wgpu_descriptor = wgpu_types::CommandEncoderDescriptor {
543551
label: Some(Cow::Owned(label.clone())),
544552
};
545553

554+
#[cfg(target_vendor = "apple")]
555+
scope.adjust_amount_of_external_allocated_memory(EXTERNAL_MEMORY_AMOUNT);
556+
546557
let (id, err) = self.instance.device_create_command_encoder(
547558
self.id,
548559
&wgpu_descriptor,
@@ -551,12 +562,39 @@ impl GPUDevice {
551562

552563
self.error_handler.push_error(err);
553564

554-
GPUCommandEncoder {
565+
let encoder = GPUCommandEncoder {
555566
instance: self.instance.clone(),
556567
error_handler: self.error_handler.clone(),
557568
id,
558569
label,
570+
#[cfg(target_vendor = "apple")]
571+
weak: std::sync::OnceLock::new(),
572+
};
573+
574+
let obj = make_cppgc_object(scope, encoder);
575+
576+
#[cfg(target_vendor = "apple")]
577+
{
578+
let finalizer = v8::Weak::with_finalizer(
579+
scope,
580+
obj,
581+
Box::new(|isolate: &mut v8::Isolate| {
582+
isolate.adjust_amount_of_external_allocated_memory(
583+
-EXTERNAL_MEMORY_AMOUNT,
584+
);
585+
}),
586+
);
587+
deno_core::cppgc::try_unwrap_cppgc_object::<GPUCommandEncoder>(
588+
scope,
589+
obj.into(),
590+
)
591+
.unwrap()
592+
.weak
593+
.set(finalizer)
594+
.unwrap();
559595
}
596+
597+
obj
560598
}
561599

562600
#[required(1)]

0 commit comments

Comments
 (0)