/
githubmirror
/
deno
Обзор
Документация
Войти
/
githubmirror
/
deno
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
ext/webgpu/compute_pass.rs
280 строк
8 KB
Luna
fix(ext/webgpu): bounds-check + view-aware setBindGroup Uint32Array fast path (#33980)
09 июн 2026, 12:30
Не верифицирован
09 июн 2026, 12:30
35d3602
Код
Авторство
О чём код?
// Copyright 2018-2026 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; use deno_core::GarbageCollected; use deno_core::WebIDL; use deno_core::cppgc::Ref; use deno_core::op2; use deno_core::v8; use deno_core::webidl::IntOptions; use deno_core::webidl::Nullable; use deno_core::webidl::WebIdlConverter; use deno_core::webidl::WebIdlError; use crate::Instance; use crate::error::GPUError; use crate::error::GPUGenericError; pub struct GPUComputePassEncoder { pub instance: Instance, pub error_handler: super::error::ErrorHandler, pub compute_pass: RefCell<wgpu_core::command::ComputePass>, pub label: String, } // SAFETY: we're sure this can be GCed unsafe impl GarbageCollected for GPUComputePassEncoder { fn trace(&self, _visitor: &mut deno_core::v8::cppgc::Visitor) {} fn get_name(&self) -> &'static std::ffi::CStr { c"GPUComputePassEncoder" } } #[op2] impl GPUComputePassEncoder { #[constructor] #[cppgc] fn constructor(_: bool) -> Result<GPUComputePassEncoder, GPUGenericError> { Err(GPUGenericError::InvalidConstructor) } #[getter] #[string] fn label(&self) -> String { self.label.clone() } #[setter] #[string] fn label(&self, #[webidl] _label: String) { // TODO(@crowlKats): no-op, needs wpgu to implement changing the label } #[undefined] fn set_pipeline( &self, #[webidl] pipeline: Ref<crate::compute_pipeline::GPUComputePipeline>, ) { let err = self .instance .compute_pass_set_pipeline( &mut self.compute_pass.borrow_mut(), pipeline.id, ) .err(); self.error_handler.push_error(err); } #[undefined] fn dispatch_workgroups( &self, #[webidl(options(enforce_range = true))] work_group_count_x: u32, #[webidl(default = 1, options(enforce_range = true))] work_group_count_y: u32, #[webidl(default = 1, options(enforce_range = true))] work_group_count_z: u32, ) { let err = self .instance .compute_pass_dispatch_workgroups( &mut self.compute_pass.borrow_mut(), work_group_count_x, work_group_count_y, work_group_count_z, ) .err(); self.error_handler.push_error(err); } #[undefined] fn dispatch_workgroups_indirect( &self, #[webidl] indirect_buffer: Ref<crate::buffer::GPUBuffer>, #[webidl(options(enforce_range = true))] indirect_offset: u64, ) { let err = self .instance .compute_pass_dispatch_workgroups_indirect( &mut self.compute_pass.borrow_mut(), indirect_buffer.id, indirect_offset, ) .err(); self.error_handler.push_error(err); } #[fast] #[undefined] fn end(&self) { let err = self .instance .compute_pass_end(&mut self.compute_pass.borrow_mut()) .err(); self.error_handler.push_error(err); } #[undefined] fn push_debug_group(&self, #[webidl] group_label: String) { let err = self .instance .compute_pass_push_debug_group( &mut self.compute_pass.borrow_mut(), &group_label, 0, // wgpu#975 ) .err(); self.error_handler.push_error(err); } #[fast] #[undefined] fn pop_debug_group(&self) { let err = self .instance .compute_pass_pop_debug_group(&mut self.compute_pass.borrow_mut()) .err(); self.error_handler.push_error(err); } #[undefined] fn insert_debug_marker(&self, #[webidl] marker_label: String) { let err = self .instance .compute_pass_insert_debug_marker( &mut self.compute_pass.borrow_mut(), &marker_label, 0, // wgpu#975 ) .err(); self.error_handler.push_error(err); } #[undefined] fn set_bind_group<'a>( &self, scope: &mut v8::PinScope<'a, '_>, #[webidl(options(enforce_range = true))] index: u32, #[webidl] bind_group: Nullable<Ref<crate::bind_group::GPUBindGroup>>, dynamic_offsets: v8::Local<'a, v8::Value>, dynamic_offsets_data_start: v8::Local<'a, v8::Value>, dynamic_offsets_data_length: v8::Local<'a, v8::Value>, ) -> Result<(), WebIdlError> { const PREFIX: &str = "Failed to execute 'setBindGroup' on 'GPUComputePassEncoder'"; let err = if let Ok(uint_32) = dynamic_offsets.try_cast::<v8::Uint32Array>() { let start = u64::convert( scope, dynamic_offsets_data_start, Cow::Borrowed(PREFIX), (|| Cow::Borrowed("Argument 4")).into(), &IntOptions { clamp: false, enforce_range: true, }, )? as usize; let len = u32::convert( scope, dynamic_offsets_data_length, Cow::Borrowed(PREFIX), (|| Cow::Borrowed("Argument 5")).into(), &IntOptions { clamp: false, enforce_range: true, }, )? as usize; let view_byte_offset = uint_32.byte_offset(); let view_len = uint_32.length(); // Validate `start..start+len` against the **view** length, not the // backing buffer's length. Without this check, `&data[start..end]` // below would panic on out-of-range input; the panic crosses the // op's `extern "C"` boundary and aborts the process. See #33956. let Some(end) = start.checked_add(len).filter(|end| *end <= view_len) else { self.error_handler.push_error(Some(GPUError::Validation(format!( "{PREFIX}: dynamicOffsetsDataStart + dynamicOffsetsDataLength ({start} + {len}) is outside the bounds of dynamicOffsetsData (length {view_len})", )))); return Ok(()); }; let ab = uint_32.buffer(scope).unwrap(); let ptr = ab.data().unwrap(); // SAFETY: `ptr` is the start of the backing ArrayBuffer's data; the // Uint32Array constructor guarantees `byte_offset + view_len * 4` // fits within `byte_length`, so the resulting slice covers exactly // the view's window. `data` is dropped at the end of this call; // `ab` keeps the backing buffer alive for that duration. // compute_pass_set_bind_group internally calls extend_from_slice // with this slice. let data = unsafe { std::slice::from_raw_parts( (ptr.as_ptr() as *const u8).add(view_byte_offset) as *const u32, view_len, ) }; let offsets = &data[start..end]; self .instance .compute_pass_set_bind_group( &mut self.compute_pass.borrow_mut(), index, bind_group.into_option().map(|bind_group| bind_group.id), offsets, ) .err() } else { let offsets = <Option<Vec<u32>>>::convert( scope, dynamic_offsets, Cow::Borrowed(PREFIX), (|| Cow::Borrowed("Argument 3")).into(), &IntOptions { clamp: false, enforce_range: true, }, )? .unwrap_or_default(); self .instance .compute_pass_set_bind_group( &mut self.compute_pass.borrow_mut(), index, bind_group.into_option().map(|bind_group| bind_group.id), &offsets, ) .err() }; self.error_handler.push_error(err); Ok(()) } } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUComputePassDescriptor { #[webidl(default = String::new())] pub label: String, pub timestamp_writes: Option<GPUComputePassTimestampWrites>, } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUComputePassTimestampWrites { pub query_set: Ref<crate::query_set::GPUQuerySet>, #[options(enforce_range = true)] pub beginning_of_pass_write_index: Option<u32>, #[options(enforce_range = true)] pub end_of_pass_write_index: Option<u32>, }