[deno] Implement ShaderModule.getCompilationInfo (#7736)

This commit is contained in:
Andy Leiserson 2025-06-11 00:49:39 -07:00 committed by GitHub
parent 1d4891ab5a
commit e9af205c71
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 246 additions and 2 deletions

View File

@ -204,6 +204,8 @@ const windowOrWorkerGlobalScope = {
GPUBindGroupLayout: util.nonEnumerable(webgpu.GPUBindGroupLayout),
GPUPipelineLayout: util.nonEnumerable(webgpu.GPUPipelineLayout),
GPUBindGroup: util.nonEnumerable(webgpu.GPUBindGroup),
GPUCompilationInfo: util.nonEnumerable(webgpu.GPUCompilationInfo),
GPUCompilationMessage: util.nonEnumerable(webgpu.GPUCompilationMessage),
GPUShaderModule: util.nonEnumerable(webgpu.GPUShaderModule),
GPUShaderStage: util.nonEnumerable(webgpu.GPUShaderStage),
GPUComputePipeline: util.nonEnumerable(webgpu.GPUComputePipeline),

View File

@ -16,8 +16,21 @@ webgpu:api,operation,rendering,color_target_state:blending,formats:*
webgpu:api,operation,rendering,color_target_state:blend_constant,setting:*
webgpu:api,operation,rendering,depth:*
webgpu:api,operation,rendering,draw:*
webgpu:api,operation,shader_module,compilation_info:*
webgpu:api,operation,uncapturederror:iff_uncaptured:*
//FAIL: webgpu:api,operation,uncapturederror:onuncapturederror_order_wrt_addEventListener
// There are also two unimplemented SKIPs in uncapturederror not enumerated here.
webgpu:api,validation,encoding,queries,general:occlusion_query,query_type:*
webgpu:shader,execution,flow_control,return:*
webgpu:shader,validation,statement,statement_behavior:invalid_statements:body="break"
webgpu:shader,validation,statement,statement_behavior:invalid_statements:body="break_if"
webgpu:shader,validation,statement,statement_behavior:invalid_statements:body="continue"
webgpu:shader,validation,statement,statement_behavior:invalid_statements:body="for3"
webgpu:shader,validation,statement,statement_behavior:invalid_statements:body="for4"
webgpu:shader,validation,statement,statement_behavior:invalid_statements:body="for5"
webgpu:shader,validation,statement,statement_behavior:invalid_statements:body="loop4"
webgpu:shader,validation,statement,statement_behavior:invalid_statements:body="loop5"
webgpu:shader,validation,statement,statement_behavior:invalid_statements:body="loop6"
webgpu:shader,validation,statement,statement_behavior:invalid_statements:body="loop8"
webgpu:shader,validation,statement,statement_behavior:invalid_statements:body="switch1"
//FAIL: 9 invalid_statements subtests due to https://github.com/gfx-rs/wgpu/issues/7733

View File

@ -16,6 +16,8 @@ import {
GPUBuffer,
GPUCommandBuffer,
GPUCommandEncoder,
GPUCompilationInfo,
GPUCompilationMessage,
GPUComputePassEncoder,
GPUComputePipeline,
GPUDevice,
@ -39,6 +41,7 @@ const {
ObjectDefineProperty,
ObjectPrototypeIsPrototypeOf,
ObjectSetPrototypeOf,
ReflectGet,
Symbol,
SymbolFor,
} = primordials;
@ -510,6 +513,58 @@ ObjectDefineProperty(GPUShaderModule, customInspect, {
});
const GPUShaderModulePrototype = GPUShaderModule.prototype;
ObjectDefineProperty(GPUCompilationInfo, customInspect, {
__proto__: null,
value(inspect, inspectOptions) {
return inspect(
createFilteredInspectProxy({
object: this,
evaluate: ObjectPrototypeIsPrototypeOf(
GPUCompilationInfoPrototype,
this,
),
keys: [
"messages",
],
}),
inspectOptions,
);
},
});
const GPUCompilationInfoPrototype = GPUCompilationInfo.prototype;
ObjectDefineProperty(GPUCompilationMessage, customInspect, {
__proto__: null,
value(inspect, inspectOptions) {
return inspect(
createFilteredInspectProxy({
object: this,
evaluate: ObjectPrototypeIsPrototypeOf(
GPUCompilationMessagePrototype,
this,
),
keys: [
"message",
"type",
"line_num",
"line_pos",
"offset",
"length",
],
}),
inspectOptions,
);
},
});
const GPUCompilationMessagePrototype = GPUCompilationMessage.prototype;
// Naming it `type` or `r#type` in Rust does not work.
// https://github.com/gfx-rs/wgpu/issues/7778
ObjectDefineProperty(GPUCompilationMessage.prototype, "type", {
get() {
return this.ty;
}
});
class GPUShaderStage {
constructor() {
webidl.illegalConstructor();
@ -722,6 +777,13 @@ ObjectDefineProperty(GPUQuerySet, customInspect, {
},
});
const GPUQuerySetPrototype = GPUQuerySet.prototype;
// Naming it `type` or `r#type` in Rust does not work.
// https://github.com/gfx-rs/wgpu/issues/7778
ObjectDefineProperty(GPUQuerySet.prototype, "type", {
get() {
return this.ty;
}
});
// Converters
@ -779,6 +841,8 @@ export {
GPUColorWrite,
GPUCommandBuffer,
GPUCommandEncoder,
GPUCompilationInfo,
GPUCompilationMessage,
GPUComputePassEncoder,
GPUComputePipeline,
GPUDevice,

View File

@ -5,6 +5,7 @@ use std::cell::RefCell;
use std::num::NonZeroU64;
use std::rc::Rc;
use deno_core::cppgc::make_cppgc_object;
use deno_core::cppgc::SameObject;
use deno_core::op2;
use deno_core::v8;
@ -33,6 +34,7 @@ use crate::error::GPUError;
use crate::query_set::GPUQuerySet;
use crate::render_bundle::GPURenderBundleEncoder;
use crate::render_pipeline::GPURenderPipeline;
use crate::shader::GPUCompilationInfo;
use crate::webidl::features_to_feature_names;
use crate::Instance;
@ -409,6 +411,7 @@ impl GPUDevice {
#[cppgc]
fn create_shader_module(
&self,
scope: &mut v8::HandleScope<'_>,
#[webidl] descriptor: super::shader::GPUShaderModuleDescriptor,
) -> GPUShaderModule {
let wgpu_descriptor = wgpu_core::pipeline::ShaderModuleDescriptor {
@ -419,16 +422,20 @@ impl GPUDevice {
let (id, err) = self.instance.device_create_shader_module(
self.id,
&wgpu_descriptor,
wgpu_core::pipeline::ShaderModuleSource::Wgsl(Cow::Owned(descriptor.code)),
wgpu_core::pipeline::ShaderModuleSource::Wgsl(Cow::Borrowed(&descriptor.code)),
None,
);
let compilation_info = GPUCompilationInfo::new(scope, err.iter(), &descriptor.code);
let compilation_info = make_cppgc_object(scope, compilation_info);
let compilation_info = v8::Global::new(scope, compilation_info);
self.error_handler.push_error(err);
GPUShaderModule {
instance: self.instance.clone(),
id,
label: descriptor.label,
compilation_info,
}
}

View File

@ -86,6 +86,8 @@ deno_core::extension!(
render_pass::GPURenderPassEncoder,
render_pipeline::GPURenderPipeline,
sampler::GPUSampler,
shader::GPUCompilationInfo,
shader::GPUCompilationMessage,
shader::GPUShaderModule,
adapter::GPUSupportedFeatures,
adapter::GPUSupportedLimits,

View File

@ -49,9 +49,11 @@ impl GPUQuerySet {
Ok(())
}
// Naming this `type` or `r#type` does not work.
// https://github.com/gfx-rs/wgpu/issues/7778
#[getter]
#[string]
fn r#type(&self) -> &'static str {
fn ty(&self) -> &'static str {
self.r#type.as_str()
}

View File

@ -1,9 +1,12 @@
// Copyright 2018-2025 the Deno authors. MIT license.
use deno_core::cppgc::make_cppgc_object;
use deno_core::op2;
use deno_core::v8;
use deno_core::webidl::WebIdlInterfaceConverter;
use deno_core::GarbageCollected;
use deno_core::WebIDL;
use wgpu_core::pipeline;
use crate::Instance;
@ -11,6 +14,7 @@ pub struct GPUShaderModule {
pub instance: Instance,
pub id: wgpu_core::id::ShaderModuleId,
pub label: String,
pub compilation_info: v8::Global<v8::Object>,
}
impl Drop for GPUShaderModule {
@ -37,6 +41,16 @@ impl GPUShaderModule {
fn label(&self, #[webidl] _label: String) {
// TODO(@crowlKats): no-op, needs wpgu to implement changing the label
}
fn get_compilation_info<'a>(
&self,
scope: &mut v8::HandleScope<'a>,
) -> v8::Local<'a, v8::Promise> {
let resolver = v8::PromiseResolver::new(scope).unwrap();
let info = v8::Local::new(scope, self.compilation_info.clone());
resolver.resolve(scope, info.into()).unwrap();
resolver.get_promise(scope)
}
}
#[derive(WebIDL)]
@ -47,3 +61,143 @@ pub(crate) struct GPUShaderModuleDescriptor {
pub code: String,
}
pub struct GPUCompilationMessage {
message: String,
r#type: GPUCompilationMessageType,
line_num: u64,
line_pos: u64,
offset: u64,
length: u64,
}
impl GarbageCollected for GPUCompilationMessage {}
#[op2]
impl GPUCompilationMessage {
#[getter]
#[string]
fn message(&self) -> String {
self.message.clone()
}
// Naming this `type` or `r#type` does not work.
// https://github.com/gfx-rs/wgpu/issues/7778
#[getter]
#[string]
fn ty(&self) -> &'static str {
self.r#type.as_str()
}
#[getter]
#[number]
fn line_num(&self) -> u64 {
self.line_num
}
#[getter]
#[number]
fn line_pos(&self) -> u64 {
self.line_pos
}
#[getter]
#[number]
fn offset(&self) -> u64 {
self.offset
}
#[getter]
#[number]
fn length(&self) -> u64 {
self.length
}
}
impl GPUCompilationMessage {
fn new(error: &pipeline::CreateShaderModuleError, source: &str) -> Self {
let message = error.to_string();
let loc = match error {
pipeline::CreateShaderModuleError::Parsing(e) => e.inner.location(source),
pipeline::CreateShaderModuleError::Validation(e) => e.inner.location(source),
_ => None,
};
match loc {
Some(loc) => {
let len_utf16 = |s: &str| s.chars().map(|c| c.len_utf16() as u64).sum();
let start = loc.offset as usize;
// Naga reports a `line_pos` using UTF-8 bytes, so we cannot use it.
let line_start = source[0..start].rfind('\n').map(|pos| pos + 1).unwrap_or(0);
let line_pos = len_utf16(&source[line_start..start]) + 1;
Self {
message,
r#type: GPUCompilationMessageType::Error,
line_num: loc.line_number.into(),
line_pos,
offset: len_utf16(&source[0..start]),
length: len_utf16(&source[start..start + loc.length as usize]),
}
}
_ => Self {
message,
r#type: GPUCompilationMessageType::Error,
line_num: 0,
line_pos: 0,
offset: 0,
length: 0,
},
}
}
}
pub struct GPUCompilationInfo {
messages: v8::Global<v8::Object>,
}
impl GarbageCollected for GPUCompilationInfo {}
#[op2]
impl GPUCompilationInfo {
#[getter]
#[global]
fn messages(&self) -> v8::Global<v8::Object> {
self.messages.clone()
}
}
impl GPUCompilationInfo {
pub fn new<'args, 'scope>(
scope: &mut v8::HandleScope<'scope>,
messages: impl ExactSizeIterator<Item = &'args pipeline::CreateShaderModuleError>,
source: &'args str,
) -> Self {
let array = v8::Array::new(scope, messages.len().try_into().unwrap());
for (i, message) in messages.enumerate() {
let message_object =
make_cppgc_object(scope, GPUCompilationMessage::new(message, source));
array.set_index(scope, i.try_into().unwrap(), message_object.into());
}
let object: v8::Local<v8::Object> = array.into();
object
.set_integrity_level(scope, v8::IntegrityLevel::Frozen)
.unwrap();
Self {
messages: v8::Global::new(scope, object),
}
}
}
#[derive(WebIDL, Clone)]
#[webidl(enum)]
pub(crate) enum GPUCompilationMessageType {
Error,
Warning,
Info,
}