1use std::cell::Cell;
7use std::os::raw::c_int;
8use std::rc::Rc;
9use std::sync::Once;
10
11use dom_struct::dom_struct;
12use js::context::JSContext;
13use mozangle::shaders::{BuiltInResources, CompileOptions, Output, ShaderValidator};
14use script_bindings::cell::DomRefCell;
15use script_bindings::domstring::TracedDOMString;
16use script_bindings::reflector::reflect_weak_referenceable_dom_object;
17use script_bindings::weakref::WeakRef;
18use servo_canvas_traits::webgl::{
19 GLLimits, GlType, WebGLCommand, WebGLError, WebGLResult, WebGLSLVersion, WebGLShaderId,
20 WebGLVersion, webgl_channel,
21};
22#[cfg(feature = "webgl")]
23use {
24 crate::dom::webgl::extensions::WebGLExtensions,
25 crate::dom::webgl::extensions::extfragdepth::EXTFragDepth,
26 crate::dom::webgl::extensions::extshadertexturelod::EXTShaderTextureLod,
27 crate::dom::webgl::extensions::oesstandardderivatives::OESStandardDerivatives,
28 crate::dom::webgl::webglobject::WebGLObject,
29 crate::dom::webgl::webglrenderingcontext::{Operation, WebGLRenderingContext},
30 crate::dom::webglrenderingcontext::capture_webgl_backtrace,
31};
32
33use crate::dom::bindings::inheritance::Castable;
34use crate::dom::bindings::reflector::DomGlobal;
35use crate::dom::bindings::root::DomRoot;
36use crate::dom::bindings::str::DOMString;
37
38#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)]
39pub(crate) enum ShaderCompilationStatus {
40 NotCompiled,
41 Succeeded,
42 Failed,
43}
44
45#[derive(JSTraceable, MallocSizeOf)]
46struct DroppableWebGLShader {
47 context: WeakRef<WebGLRenderingContext>,
48 #[no_trace]
49 id: WebGLShaderId,
50 marked_for_deletion: Cell<bool>,
51}
52
53impl DroppableWebGLShader {
54 fn send_with_fallibility(&self, command: WebGLCommand, fallibility: Operation) {
55 if let Some(root) = self.context.root() {
56 let result = root.sender().send(command, capture_webgl_backtrace());
57 if matches!(fallibility, Operation::Infallible) {
58 result.expect("Operation failed");
59 }
60 }
61 }
62
63 fn mark_for_deletion(&self, operation_fallibility: Operation) {
64 if !self.marked_for_deletion.get() {
65 self.marked_for_deletion.set(true);
66 self.send_with_fallibility(WebGLCommand::DeleteShader(self.id), operation_fallibility);
67 }
68 }
69}
70
71impl Drop for DroppableWebGLShader {
72 fn drop(&mut self) {
73 self.mark_for_deletion(Operation::Fallible);
74 }
75}
76
77#[dom_struct]
78pub(crate) struct WebGLShader {
79 webgl_object: WebGLObject,
80 gl_type: u32,
81 source: DomRefCell<TracedDOMString>,
82 info_log: DomRefCell<TracedDOMString>,
83 attached_counter: Cell<u32>,
84 compilation_status: Cell<ShaderCompilationStatus>,
85 droppable: DroppableWebGLShader,
86}
87
88static GLSLANG_INITIALIZATION: Once = Once::new();
89
90impl WebGLShader {
91 fn new_inherited(context: &WebGLRenderingContext, id: WebGLShaderId, shader_type: u32) -> Self {
92 GLSLANG_INITIALIZATION.call_once(|| ::mozangle::shaders::initialize().unwrap());
93 Self {
94 webgl_object: WebGLObject::new_inherited(context),
95 gl_type: shader_type,
96 source: Default::default(),
97 info_log: Default::default(),
98 attached_counter: Cell::new(0),
99 compilation_status: Cell::new(ShaderCompilationStatus::NotCompiled),
100 droppable: DroppableWebGLShader {
101 context: WeakRef::new(context),
102 id,
103 marked_for_deletion: Cell::new(false),
104 },
105 }
106 }
107
108 pub(crate) fn maybe_new(
109 cx: &mut JSContext,
110 context: &WebGLRenderingContext,
111 shader_type: u32,
112 ) -> Option<DomRoot<Self>> {
113 let (sender, receiver) = webgl_channel().unwrap();
114 context.send_command(WebGLCommand::CreateShader(shader_type, sender));
115 receiver
116 .recv()
117 .unwrap()
118 .map(|id| WebGLShader::new(cx, context, id, shader_type))
119 }
120
121 pub(crate) fn new(
122 cx: &mut JSContext,
123 context: &WebGLRenderingContext,
124 id: WebGLShaderId,
125 shader_type: u32,
126 ) -> DomRoot<Self> {
127 reflect_weak_referenceable_dom_object(
128 cx,
129 Rc::new(WebGLShader::new_inherited(context, id, shader_type)),
130 &*context.global(),
131 )
132 }
133}
134
135impl WebGLShader {
136 pub(crate) fn id(&self) -> WebGLShaderId {
137 self.droppable.id
138 }
139
140 pub(crate) fn gl_type(&self) -> u32 {
141 self.gl_type
142 }
143
144 pub(crate) fn compile(
146 &self,
147 api_type: GlType,
148 webgl_version: WebGLVersion,
149 glsl_version: WebGLSLVersion,
150 limits: &GLLimits,
151 ext: &WebGLExtensions,
152 ) -> WebGLResult<()> {
153 if self.droppable.marked_for_deletion.get() && !self.is_attached() {
154 return Err(WebGLError::InvalidValue);
155 }
156 if self.compilation_status.get() != ShaderCompilationStatus::NotCompiled {
157 debug!("Compiling already compiled shader {}", self.id());
158 }
159
160 let source = self.source.borrow();
161
162 let mut params = BuiltInResources {
163 MaxVertexAttribs: limits.max_vertex_attribs as c_int,
164 MaxVertexUniformVectors: limits.max_vertex_uniform_vectors as c_int,
165 MaxVertexTextureImageUnits: limits.max_vertex_texture_image_units as c_int,
166 MaxCombinedTextureImageUnits: limits.max_combined_texture_image_units as c_int,
167 MaxTextureImageUnits: limits.max_texture_image_units as c_int,
168 MaxFragmentUniformVectors: limits.max_fragment_uniform_vectors as c_int,
169
170 MaxVertexOutputVectors: limits.max_vertex_output_vectors as c_int,
171 MaxFragmentInputVectors: limits.max_fragment_input_vectors as c_int,
172 MaxVaryingVectors: limits.max_varying_vectors as c_int,
173
174 OES_standard_derivatives: ext.is_enabled::<OESStandardDerivatives>() as c_int,
175 EXT_shader_texture_lod: ext.is_enabled::<EXTShaderTextureLod>() as c_int,
176 EXT_frag_depth: ext.is_enabled::<EXTFragDepth>() as c_int,
177
178 FragmentPrecisionHigh: 1,
179 ..Default::default()
180 };
181
182 if webgl_version == WebGLVersion::WebGL2 {
183 params.MinProgramTexelOffset = limits.min_program_texel_offset as c_int;
184 params.MaxProgramTexelOffset = limits.max_program_texel_offset as c_int;
185 params.MaxDrawBuffers = limits.max_draw_buffers as c_int;
186 }
187
188 let validator = match webgl_version {
189 WebGLVersion::WebGL1 => {
190 let output_format = if api_type == GlType::Gles {
191 Output::Essl
192 } else {
193 Output::Glsl
194 };
195 ShaderValidator::for_webgl(self.gl_type, output_format, ¶ms).unwrap()
196 },
197 WebGLVersion::WebGL2 => {
198 let output_format = if api_type == GlType::Gles {
199 Output::Essl
200 } else {
201 match (glsl_version.major, glsl_version.minor) {
202 (1, 30) => Output::Glsl130,
203 (1, 40) => Output::Glsl140,
204 (1, 50) => Output::Glsl150Core,
205 (3, 30) => Output::Glsl330Core,
206 (4, 0) => Output::Glsl400Core,
207 (4, 10) => Output::Glsl410Core,
208 (4, 20) => Output::Glsl420Core,
209 (4, 30) => Output::Glsl430Core,
210 (4, 40) => Output::Glsl440Core,
211 (4, _) => Output::Glsl450Core,
212 _ => Output::Glsl140,
213 }
214 };
215 ShaderValidator::for_webgl2(self.gl_type, output_format, ¶ms).unwrap()
216 },
217 };
218
219 let mut options = CompileOptions::mozangle();
222 options.set_variables(1);
223 options.set_enforcePackingRestrictions(1);
224 options.set_objectCode(1);
225 options.set_initGLPosition(1);
226 options.set_initializeUninitializedLocals(1);
227 options.set_initOutputVariables(1);
228
229 options.set_limitExpressionComplexity(1);
230 options.set_limitCallStackDepth(1);
231
232 if cfg!(target_os = "macos") {
233 options.set_removeInvariantAndCentroidForESSL3(1);
234
235 options.set_unfoldShortCircuit(1);
238 options.set_regenerateStructNames(1);
240 options.set_addAndTrueToLoopCondition(1);
243 options.set_rewriteTexelFetchOffsetToTexelFetch(1);
244 } else {
245 options.set_clampIndirectArrayBounds(1);
249 }
250
251 match validator.compile(&[&source.str()], options) {
252 Ok(()) => {
253 let translated_source = validator.object_code();
254 debug!("Shader translated: {}", translated_source);
255 self.upcast()
259 .send_command(WebGLCommand::CompileShader(self.id(), translated_source));
260 self.compilation_status
261 .set(ShaderCompilationStatus::Succeeded);
262 },
263 Err(error) => {
264 self.compilation_status.set(ShaderCompilationStatus::Failed);
265 debug!("Shader {} compilation failed: {}", self.id(), error);
266 },
267 }
268
269 *self.info_log.borrow_mut() = validator.info_log().into();
270
271 Ok(())
272 }
273
274 pub(crate) fn mark_for_deletion(&self, operation_fallibility: Operation) {
278 self.droppable.mark_for_deletion(operation_fallibility);
279 }
280
281 pub(crate) fn is_marked_for_deletion(&self) -> bool {
282 self.droppable.marked_for_deletion.get()
283 }
284
285 pub(crate) fn is_deleted(&self) -> bool {
286 self.droppable.marked_for_deletion.get() && !self.is_attached()
287 }
288
289 pub(crate) fn is_attached(&self) -> bool {
290 self.attached_counter.get() > 0
291 }
292
293 pub(crate) fn increment_attached_counter(&self) {
294 self.attached_counter.set(self.attached_counter.get() + 1);
295 }
296
297 pub(crate) fn decrement_attached_counter(&self) {
298 assert!(self.attached_counter.get() > 0);
299 self.attached_counter.set(self.attached_counter.get() - 1);
300 }
301
302 pub(crate) fn info_log(&self) -> DOMString {
304 self.info_log.borrow().clone().root()
305 }
306
307 pub(crate) fn source(&self) -> DOMString {
309 self.source.borrow().clone().root()
310 }
311
312 pub(crate) fn set_source(&self, source: DOMString) {
314 *self.source.borrow_mut() = source.traced();
315 }
316
317 pub(crate) fn successfully_compiled(&self) -> bool {
318 self.compilation_status.get() == ShaderCompilationStatus::Succeeded
319 }
320}