Skip to content

Commit e9af205

Browse files
[deno] Implement ShaderModule.getCompilationInfo (gfx-rs#7736)
1 parent 1d4891a commit e9af205

7 files changed

Lines changed: 246 additions & 2 deletions

File tree

cts_runner/src/bootstrap.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,8 @@ const windowOrWorkerGlobalScope = {
204204
GPUBindGroupLayout: util.nonEnumerable(webgpu.GPUBindGroupLayout),
205205
GPUPipelineLayout: util.nonEnumerable(webgpu.GPUPipelineLayout),
206206
GPUBindGroup: util.nonEnumerable(webgpu.GPUBindGroup),
207+
GPUCompilationInfo: util.nonEnumerable(webgpu.GPUCompilationInfo),
208+
GPUCompilationMessage: util.nonEnumerable(webgpu.GPUCompilationMessage),
207209
GPUShaderModule: util.nonEnumerable(webgpu.GPUShaderModule),
208210
GPUShaderStage: util.nonEnumerable(webgpu.GPUShaderStage),
209211
GPUComputePipeline: util.nonEnumerable(webgpu.GPUComputePipeline),

cts_runner/test.lst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,21 @@ webgpu:api,operation,rendering,color_target_state:blending,formats:*
1616
webgpu:api,operation,rendering,color_target_state:blend_constant,setting:*
1717
webgpu:api,operation,rendering,depth:*
1818
webgpu:api,operation,rendering,draw:*
19+
webgpu:api,operation,shader_module,compilation_info:*
1920
webgpu:api,operation,uncapturederror:iff_uncaptured:*
2021
//FAIL: webgpu:api,operation,uncapturederror:onuncapturederror_order_wrt_addEventListener
2122
// There are also two unimplemented SKIPs in uncapturederror not enumerated here.
2223
webgpu:api,validation,encoding,queries,general:occlusion_query,query_type:*
2324
webgpu:shader,execution,flow_control,return:*
25+
webgpu:shader,validation,statement,statement_behavior:invalid_statements:body="break"
26+
webgpu:shader,validation,statement,statement_behavior:invalid_statements:body="break_if"
27+
webgpu:shader,validation,statement,statement_behavior:invalid_statements:body="continue"
28+
webgpu:shader,validation,statement,statement_behavior:invalid_statements:body="for3"
29+
webgpu:shader,validation,statement,statement_behavior:invalid_statements:body="for4"
30+
webgpu:shader,validation,statement,statement_behavior:invalid_statements:body="for5"
31+
webgpu:shader,validation,statement,statement_behavior:invalid_statements:body="loop4"
32+
webgpu:shader,validation,statement,statement_behavior:invalid_statements:body="loop5"
33+
webgpu:shader,validation,statement,statement_behavior:invalid_statements:body="loop6"
34+
webgpu:shader,validation,statement,statement_behavior:invalid_statements:body="loop8"
35+
webgpu:shader,validation,statement,statement_behavior:invalid_statements:body="switch1"
36+
//FAIL: 9 invalid_statements subtests due to https://github.com/gfx-rs/wgpu/issues/7733

deno_webgpu/01_webgpu.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import {
1616
GPUBuffer,
1717
GPUCommandBuffer,
1818
GPUCommandEncoder,
19+
GPUCompilationInfo,
20+
GPUCompilationMessage,
1921
GPUComputePassEncoder,
2022
GPUComputePipeline,
2123
GPUDevice,
@@ -39,6 +41,7 @@ const {
3941
ObjectDefineProperty,
4042
ObjectPrototypeIsPrototypeOf,
4143
ObjectSetPrototypeOf,
44+
ReflectGet,
4245
Symbol,
4346
SymbolFor,
4447
} = primordials;
@@ -510,6 +513,58 @@ ObjectDefineProperty(GPUShaderModule, customInspect, {
510513
});
511514
const GPUShaderModulePrototype = GPUShaderModule.prototype;
512515

516+
ObjectDefineProperty(GPUCompilationInfo, customInspect, {
517+
__proto__: null,
518+
value(inspect, inspectOptions) {
519+
return inspect(
520+
createFilteredInspectProxy({
521+
object: this,
522+
evaluate: ObjectPrototypeIsPrototypeOf(
523+
GPUCompilationInfoPrototype,
524+
this,
525+
),
526+
keys: [
527+
"messages",
528+
],
529+
}),
530+
inspectOptions,
531+
);
532+
},
533+
});
534+
const GPUCompilationInfoPrototype = GPUCompilationInfo.prototype;
535+
536+
ObjectDefineProperty(GPUCompilationMessage, customInspect, {
537+
__proto__: null,
538+
value(inspect, inspectOptions) {
539+
return inspect(
540+
createFilteredInspectProxy({
541+
object: this,
542+
evaluate: ObjectPrototypeIsPrototypeOf(
543+
GPUCompilationMessagePrototype,
544+
this,
545+
),
546+
keys: [
547+
"message",
548+
"type",
549+
"line_num",
550+
"line_pos",
551+
"offset",
552+
"length",
553+
],
554+
}),
555+
inspectOptions,
556+
);
557+
},
558+
});
559+
const GPUCompilationMessagePrototype = GPUCompilationMessage.prototype;
560+
// Naming it `type` or `r#type` in Rust does not work.
561+
// https://github.com/gfx-rs/wgpu/issues/7778
562+
ObjectDefineProperty(GPUCompilationMessage.prototype, "type", {
563+
get() {
564+
return this.ty;
565+
}
566+
});
567+
513568
class GPUShaderStage {
514569
constructor() {
515570
webidl.illegalConstructor();
@@ -722,6 +777,13 @@ ObjectDefineProperty(GPUQuerySet, customInspect, {
722777
},
723778
});
724779
const GPUQuerySetPrototype = GPUQuerySet.prototype;
780+
// Naming it `type` or `r#type` in Rust does not work.
781+
// https://github.com/gfx-rs/wgpu/issues/7778
782+
ObjectDefineProperty(GPUQuerySet.prototype, "type", {
783+
get() {
784+
return this.ty;
785+
}
786+
});
725787

726788
// Converters
727789

@@ -779,6 +841,8 @@ export {
779841
GPUColorWrite,
780842
GPUCommandBuffer,
781843
GPUCommandEncoder,
844+
GPUCompilationInfo,
845+
GPUCompilationMessage,
782846
GPUComputePassEncoder,
783847
GPUComputePipeline,
784848
GPUDevice,

deno_webgpu/device.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +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;
89
use deno_core::cppgc::SameObject;
910
use deno_core::op2;
1011
use deno_core::v8;
@@ -33,6 +34,7 @@ use crate::error::GPUError;
3334
use crate::query_set::GPUQuerySet;
3435
use crate::render_bundle::GPURenderBundleEncoder;
3536
use crate::render_pipeline::GPURenderPipeline;
37+
use crate::shader::GPUCompilationInfo;
3638
use crate::webidl::features_to_feature_names;
3739
use crate::Instance;
3840

@@ -409,6 +411,7 @@ impl GPUDevice {
409411
#[cppgc]
410412
fn create_shader_module(
411413
&self,
414+
scope: &mut v8::HandleScope<'_>,
412415
#[webidl] descriptor: super::shader::GPUShaderModuleDescriptor,
413416
) -> GPUShaderModule {
414417
let wgpu_descriptor = wgpu_core::pipeline::ShaderModuleDescriptor {
@@ -419,16 +422,20 @@ impl GPUDevice {
419422
let (id, err) = self.instance.device_create_shader_module(
420423
self.id,
421424
&wgpu_descriptor,
422-
wgpu_core::pipeline::ShaderModuleSource::Wgsl(Cow::Owned(descriptor.code)),
425+
wgpu_core::pipeline::ShaderModuleSource::Wgsl(Cow::Borrowed(&descriptor.code)),
423426
None,
424427
);
425428

429+
let compilation_info = GPUCompilationInfo::new(scope, err.iter(), &descriptor.code);
430+
let compilation_info = make_cppgc_object(scope, compilation_info);
431+
let compilation_info = v8::Global::new(scope, compilation_info);
426432
self.error_handler.push_error(err);
427433

428434
GPUShaderModule {
429435
instance: self.instance.clone(),
430436
id,
431437
label: descriptor.label,
438+
compilation_info,
432439
}
433440
}
434441

deno_webgpu/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ deno_core::extension!(
8686
render_pass::GPURenderPassEncoder,
8787
render_pipeline::GPURenderPipeline,
8888
sampler::GPUSampler,
89+
shader::GPUCompilationInfo,
90+
shader::GPUCompilationMessage,
8991
shader::GPUShaderModule,
9092
adapter::GPUSupportedFeatures,
9193
adapter::GPUSupportedLimits,

deno_webgpu/query_set.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,11 @@ impl GPUQuerySet {
4949
Ok(())
5050
}
5151

52+
// Naming this `type` or `r#type` does not work.
53+
// https://github.com/gfx-rs/wgpu/issues/7778
5254
#[getter]
5355
#[string]
54-
fn r#type(&self) -> &'static str {
56+
fn ty(&self) -> &'static str {
5557
self.r#type.as_str()
5658
}
5759

deno_webgpu/shader.rs

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
// Copyright 2018-2025 the Deno authors. MIT license.
22

3+
use deno_core::cppgc::make_cppgc_object;
34
use deno_core::op2;
5+
use deno_core::v8;
46
use deno_core::webidl::WebIdlInterfaceConverter;
57
use deno_core::GarbageCollected;
68
use deno_core::WebIDL;
9+
use wgpu_core::pipeline;
710

811
use crate::Instance;
912

1013
pub struct GPUShaderModule {
1114
pub instance: Instance,
1215
pub id: wgpu_core::id::ShaderModuleId,
1316
pub label: String,
17+
pub compilation_info: v8::Global<v8::Object>,
1418
}
1519

1620
impl Drop for GPUShaderModule {
@@ -37,6 +41,16 @@ impl GPUShaderModule {
3741
fn label(&self, #[webidl] _label: String) {
3842
// TODO(@crowlKats): no-op, needs wpgu to implement changing the label
3943
}
44+
45+
fn get_compilation_info<'a>(
46+
&self,
47+
scope: &mut v8::HandleScope<'a>,
48+
) -> v8::Local<'a, v8::Promise> {
49+
let resolver = v8::PromiseResolver::new(scope).unwrap();
50+
let info = v8::Local::new(scope, self.compilation_info.clone());
51+
resolver.resolve(scope, info.into()).unwrap();
52+
resolver.get_promise(scope)
53+
}
4054
}
4155

4256
#[derive(WebIDL)]
@@ -47,3 +61,143 @@ pub(crate) struct GPUShaderModuleDescriptor {
4761

4862
pub code: String,
4963
}
64+
65+
pub struct GPUCompilationMessage {
66+
message: String,
67+
r#type: GPUCompilationMessageType,
68+
line_num: u64,
69+
line_pos: u64,
70+
offset: u64,
71+
length: u64,
72+
}
73+
74+
impl GarbageCollected for GPUCompilationMessage {}
75+
76+
#[op2]
77+
impl GPUCompilationMessage {
78+
#[getter]
79+
#[string]
80+
fn message(&self) -> String {
81+
self.message.clone()
82+
}
83+
84+
// Naming this `type` or `r#type` does not work.
85+
// https://github.com/gfx-rs/wgpu/issues/7778
86+
#[getter]
87+
#[string]
88+
fn ty(&self) -> &'static str {
89+
self.r#type.as_str()
90+
}
91+
92+
#[getter]
93+
#[number]
94+
fn line_num(&self) -> u64 {
95+
self.line_num
96+
}
97+
98+
#[getter]
99+
#[number]
100+
fn line_pos(&self) -> u64 {
101+
self.line_pos
102+
}
103+
104+
#[getter]
105+
#[number]
106+
fn offset(&self) -> u64 {
107+
self.offset
108+
}
109+
110+
#[getter]
111+
#[number]
112+
fn length(&self) -> u64 {
113+
self.length
114+
}
115+
}
116+
117+
impl GPUCompilationMessage {
118+
fn new(error: &pipeline::CreateShaderModuleError, source: &str) -> Self {
119+
let message = error.to_string();
120+
121+
let loc = match error {
122+
pipeline::CreateShaderModuleError::Parsing(e) => e.inner.location(source),
123+
pipeline::CreateShaderModuleError::Validation(e) => e.inner.location(source),
124+
_ => None,
125+
};
126+
127+
match loc {
128+
Some(loc) => {
129+
let len_utf16 = |s: &str| s.chars().map(|c| c.len_utf16() as u64).sum();
130+
131+
let start = loc.offset as usize;
132+
133+
// Naga reports a `line_pos` using UTF-8 bytes, so we cannot use it.
134+
let line_start = source[0..start].rfind('\n').map(|pos| pos + 1).unwrap_or(0);
135+
let line_pos = len_utf16(&source[line_start..start]) + 1;
136+
137+
Self {
138+
message,
139+
r#type: GPUCompilationMessageType::Error,
140+
line_num: loc.line_number.into(),
141+
line_pos,
142+
offset: len_utf16(&source[0..start]),
143+
length: len_utf16(&source[start..start + loc.length as usize]),
144+
}
145+
}
146+
_ => Self {
147+
message,
148+
r#type: GPUCompilationMessageType::Error,
149+
line_num: 0,
150+
line_pos: 0,
151+
offset: 0,
152+
length: 0,
153+
},
154+
}
155+
}
156+
}
157+
158+
pub struct GPUCompilationInfo {
159+
messages: v8::Global<v8::Object>,
160+
}
161+
162+
impl GarbageCollected for GPUCompilationInfo {}
163+
164+
#[op2]
165+
impl GPUCompilationInfo {
166+
#[getter]
167+
#[global]
168+
fn messages(&self) -> v8::Global<v8::Object> {
169+
self.messages.clone()
170+
}
171+
}
172+
173+
impl GPUCompilationInfo {
174+
pub fn new<'args, 'scope>(
175+
scope: &mut v8::HandleScope<'scope>,
176+
messages: impl ExactSizeIterator<Item = &'args pipeline::CreateShaderModuleError>,
177+
source: &'args str,
178+
) -> Self {
179+
let array = v8::Array::new(scope, messages.len().try_into().unwrap());
180+
for (i, message) in messages.enumerate() {
181+
let message_object =
182+
make_cppgc_object(scope, GPUCompilationMessage::new(message, source));
183+
array.set_index(scope, i.try_into().unwrap(), message_object.into());
184+
}
185+
186+
let object: v8::Local<v8::Object> = array.into();
187+
object
188+
.set_integrity_level(scope, v8::IntegrityLevel::Frozen)
189+
.unwrap();
190+
191+
Self {
192+
messages: v8::Global::new(scope, object),
193+
}
194+
}
195+
}
196+
197+
#[derive(WebIDL, Clone)]
198+
#[webidl(enum)]
199+
pub(crate) enum GPUCompilationMessageType {
200+
Error,
201+
Warning,
202+
Info,
203+
}

0 commit comments

Comments
 (0)