aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/webglshader.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/dom/webglshader.rs')
-rw-r--r--components/script/dom/webglshader.rs408
1 files changed, 308 insertions, 100 deletions
diff --git a/components/script/dom/webglshader.rs b/components/script/dom/webglshader.rs
index b66151bf353..3f5f6c1cf43 100644
--- a/components/script/dom/webglshader.rs
+++ b/components/script/dom/webglshader.rs
@@ -1,25 +1,29 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
// https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
-use angle::hl::{BuiltInResources, Output, ShaderValidator};
-use canvas_traits::CanvasMsg;
-use dom::bindings::cell::DOMRefCell;
-use dom::bindings::codegen::Bindings::WebGLShaderBinding;
-use dom::bindings::js::Root;
-use dom::bindings::reflector::reflect_dom_object;
-use dom::bindings::str::DOMString;
-use dom::webglobject::WebGLObject;
-use dom::window::Window;
+use crate::dom::bindings::cell::DomRefCell;
+use crate::dom::bindings::inheritance::Castable;
+use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
+use crate::dom::bindings::root::DomRoot;
+use crate::dom::bindings::str::DOMString;
+use crate::dom::webgl_extensions::ext::extfragdepth::EXTFragDepth;
+use crate::dom::webgl_extensions::ext::extshadertexturelod::EXTShaderTextureLod;
+use crate::dom::webgl_extensions::ext::oesstandardderivatives::OESStandardDerivatives;
+use crate::dom::webgl_extensions::WebGLExtensions;
+use crate::dom::webglobject::WebGLObject;
+use crate::dom::webglrenderingcontext::{Operation, WebGLRenderingContext};
+use canvas_traits::webgl::{webgl_channel, GlType, WebGLVersion};
+use canvas_traits::webgl::{GLLimits, WebGLCommand, WebGLError};
+use canvas_traits::webgl::{WebGLResult, WebGLSLVersion, WebGLShaderId};
use dom_struct::dom_struct;
-use ipc_channel::ipc::IpcSender;
+use mozangle::shaders::{ffi, BuiltInResources, Output, ShaderValidator};
use std::cell::Cell;
-use std::sync::{ONCE_INIT, Once};
-use webrender_traits;
-use webrender_traits::{WebGLCommand, WebGLParameter, WebGLResult, WebGLShaderId};
+use std::os::raw::c_int;
+use std::sync::Once;
-#[derive(Clone, Copy, PartialEq, Debug, JSTraceable, HeapSizeOf)]
+#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)]
pub enum ShaderCompilationStatus {
NotCompiled,
Succeeded,
@@ -31,64 +35,165 @@ pub struct WebGLShader {
webgl_object: WebGLObject,
id: WebGLShaderId,
gl_type: u32,
- source: DOMRefCell<Option<DOMString>>,
- info_log: DOMRefCell<Option<String>>,
- is_deleted: Cell<bool>,
+ source: DomRefCell<DOMString>,
+ info_log: DomRefCell<DOMString>,
+ marked_for_deletion: Cell<bool>,
attached_counter: Cell<u32>,
compilation_status: Cell<ShaderCompilationStatus>,
- #[ignore_heap_size_of = "Defined in ipc-channel"]
- renderer: IpcSender<CanvasMsg>,
}
-#[cfg(not(target_os = "android"))]
-const SHADER_OUTPUT_FORMAT: Output = Output::Glsl;
-
-#[cfg(target_os = "android")]
-const SHADER_OUTPUT_FORMAT: Output = Output::Essl;
-
-static GLSLANG_INITIALIZATION: Once = ONCE_INIT;
+static GLSLANG_INITIALIZATION: Once = Once::new();
impl WebGLShader {
- fn new_inherited(renderer: IpcSender<CanvasMsg>,
- id: WebGLShaderId,
- shader_type: u32)
- -> WebGLShader {
- GLSLANG_INITIALIZATION.call_once(|| ::angle::hl::initialize().unwrap());
- WebGLShader {
- webgl_object: WebGLObject::new_inherited(),
+ fn new_inherited(context: &WebGLRenderingContext, id: WebGLShaderId, shader_type: u32) -> Self {
+ GLSLANG_INITIALIZATION.call_once(|| ::mozangle::shaders::initialize().unwrap());
+ Self {
+ webgl_object: WebGLObject::new_inherited(context),
id: id,
gl_type: shader_type,
- source: DOMRefCell::new(None),
- info_log: DOMRefCell::new(None),
- is_deleted: Cell::new(false),
+ source: Default::default(),
+ info_log: Default::default(),
+ marked_for_deletion: Cell::new(false),
attached_counter: Cell::new(0),
compilation_status: Cell::new(ShaderCompilationStatus::NotCompiled),
- renderer: renderer,
}
}
- pub fn maybe_new(window: &Window,
- renderer: IpcSender<CanvasMsg>,
- shader_type: u32)
- -> Option<Root<WebGLShader>> {
- let (sender, receiver) = webrender_traits::channel::msg_channel().unwrap();
- renderer.send(CanvasMsg::WebGL(WebGLCommand::CreateShader(shader_type, sender))).unwrap();
-
- let result = receiver.recv().unwrap();
- result.map(|shader_id| WebGLShader::new(window, renderer, shader_id, shader_type))
+ pub fn maybe_new(context: &WebGLRenderingContext, shader_type: u32) -> Option<DomRoot<Self>> {
+ let (sender, receiver) = webgl_channel().unwrap();
+ context.send_command(WebGLCommand::CreateShader(shader_type, sender));
+ receiver
+ .recv()
+ .unwrap()
+ .map(|id| WebGLShader::new(context, id, shader_type))
}
- pub fn new(window: &Window,
- renderer: IpcSender<CanvasMsg>,
- id: WebGLShaderId,
- shader_type: u32)
- -> Root<WebGLShader> {
- reflect_dom_object(box WebGLShader::new_inherited(renderer, id, shader_type),
- window,
- WebGLShaderBinding::Wrap)
+ pub fn new(
+ context: &WebGLRenderingContext,
+ id: WebGLShaderId,
+ shader_type: u32,
+ ) -> DomRoot<Self> {
+ reflect_dom_object(
+ Box::new(WebGLShader::new_inherited(context, id, shader_type)),
+ &*context.global(),
+ )
}
}
+// Based on https://searchfox.org/mozilla-central/rev/efdf9bb55789ea782ae3a431bda6be74a87b041e/gfx/angle/checkout/src/compiler/translator/ShaderLang.cpp#173
+fn default_validator() -> BuiltInResources {
+ BuiltInResources {
+ // Constants.
+ MaxVertexAttribs: 8,
+ MaxVertexUniformVectors: 128,
+ MaxVaryingVectors: 8,
+ MaxVertexTextureImageUnits: 0,
+ MaxCombinedTextureImageUnits: 8,
+ MaxTextureImageUnits: 8,
+ MaxFragmentUniformVectors: 16,
+ MaxDrawBuffers: 1,
+
+ // Extensions.
+ OES_standard_derivatives: 0,
+ OES_EGL_image_external: 0,
+ OES_EGL_image_external_essl3: 0,
+ NV_EGL_stream_consumer_external: 0,
+ ARB_texture_rectangle: 0,
+ EXT_blend_func_extended: 0,
+ EXT_draw_buffers: 0,
+ EXT_frag_depth: 0,
+ EXT_shader_texture_lod: 0,
+ WEBGL_debug_shader_precision: 0,
+ EXT_shader_framebuffer_fetch: 0,
+ NV_shader_framebuffer_fetch: 0,
+ NV_draw_buffers: 0,
+ ARM_shader_framebuffer_fetch: 0,
+ //OVR_multiview: 0,
+ OVR_multiview2: 0,
+ EXT_YUV_target: 0,
+ EXT_geometry_shader: 0,
+ OES_texture_storage_multisample_2d_array: 0,
+ //OES_texture_3d: 0,
+ ANGLE_texture_multisample: 0,
+ ANGLE_multi_draw: 0,
+
+ // Disable highp precision in fragment shader by default.
+ FragmentPrecisionHigh: 0,
+
+ // GLSL ES 3.0 constants.
+ MaxVertexOutputVectors: 16,
+ MaxFragmentInputVectors: 15,
+ MinProgramTexelOffset: -8,
+ MaxProgramTexelOffset: 7,
+
+ // Extension constants.
+ MaxDualSourceDrawBuffers: 0,
+ MaxViewsOVR: 4,
+
+ // Disable name hashing by default.
+ HashFunction: None,
+ ArrayIndexClampingStrategy:
+ ffi::ShArrayIndexClampingStrategy::SH_CLAMP_WITH_CLAMP_INTRINSIC,
+
+ MaxExpressionComplexity: 256,
+ MaxCallStackDepth: 256,
+ MaxFunctionParameters: 1024,
+
+ // ES 3.1 Revision 4, 7.2 Built-in Constants
+
+ // ES 3.1, Revision 4, 8.13 Texture minification
+ // "The value of MIN_PROGRAM_TEXTURE_GATHER_OFFSET must be less than or equal to the value of
+ // MIN_PROGRAM_TEXEL_OFFSET. The value of MAX_PROGRAM_TEXTURE_GATHER_OFFSET must be greater than
+ // or equal to the value of MAX_PROGRAM_TEXEL_OFFSET"
+ MinProgramTextureGatherOffset: -8,
+ MaxProgramTextureGatherOffset: 7,
+
+ MaxImageUnits: 4,
+ MaxVertexImageUniforms: 0,
+ MaxFragmentImageUniforms: 0,
+ MaxComputeImageUniforms: 0,
+ MaxCombinedImageUniforms: 0,
+
+ MaxUniformLocations: 1024,
+
+ MaxCombinedShaderOutputResources: 4,
+
+ MaxComputeWorkGroupCount: [65535, 65535, 65535],
+ MaxComputeWorkGroupSize: [128, 128, 64],
+ MaxComputeUniformComponents: 512,
+ MaxComputeTextureImageUnits: 16,
+
+ MaxComputeAtomicCounters: 8,
+ MaxComputeAtomicCounterBuffers: 1,
+
+ MaxVertexAtomicCounters: 0,
+ MaxFragmentAtomicCounters: 0,
+ MaxCombinedAtomicCounters: 8,
+ MaxAtomicCounterBindings: 1,
+
+ MaxVertexAtomicCounterBuffers: 0,
+ MaxFragmentAtomicCounterBuffers: 0,
+ MaxCombinedAtomicCounterBuffers: 1,
+ MaxAtomicCounterBufferSize: 32,
+
+ MaxUniformBufferBindings: 32,
+ MaxShaderStorageBufferBindings: 4,
+ MaxPointSize: 0.0,
+
+ MaxGeometryUniformComponents: 1024,
+ MaxGeometryUniformBlocks: 12,
+ MaxGeometryInputComponents: 64,
+ MaxGeometryOutputComponents: 64,
+ MaxGeometryOutputVertices: 256,
+ MaxGeometryTotalOutputComponents: 1024,
+ MaxGeometryTextureImageUnits: 16,
+ MaxGeometryAtomicCounterBuffers: 0,
+ MaxGeometryAtomicCounters: 0,
+ MaxGeometryShaderStorageBlocks: 0,
+ MaxGeometryShaderInvocations: 32,
+ MaxGeometryImageUniforms: 0,
+ }
+}
impl WebGLShader {
pub fn id(&self) -> WebGLShaderId {
@@ -100,54 +205,165 @@ impl WebGLShader {
}
/// glCompileShader
- pub fn compile(&self) {
+ pub fn compile(
+ &self,
+ api_type: GlType,
+ webgl_version: WebGLVersion,
+ glsl_version: WebGLSLVersion,
+ limits: &GLLimits,
+ ext: &WebGLExtensions,
+ ) -> WebGLResult<()> {
+ if self.marked_for_deletion.get() && !self.is_attached() {
+ return Err(WebGLError::InvalidValue);
+ }
if self.compilation_status.get() != ShaderCompilationStatus::NotCompiled {
debug!("Compiling already compiled shader {}", self.id);
}
- if let Some(ref source) = *self.source.borrow() {
- let mut params = BuiltInResources::default();
- params.FragmentPrecisionHigh = 1;
- let validator = ShaderValidator::for_webgl(self.gl_type,
- SHADER_OUTPUT_FORMAT,
- &params).unwrap();
- match validator.compile_and_translate(&[source]) {
- Ok(translated_source) => {
- debug!("Shader translated: {}", translated_source);
- // NOTE: At this point we should be pretty sure that the compilation in the paint thread
- // will succeed.
- // It could be interesting to retrieve the info log from the paint thread though
- let msg = WebGLCommand::CompileShader(self.id, translated_source);
- self.renderer.send(CanvasMsg::WebGL(msg)).unwrap();
- self.compilation_status.set(ShaderCompilationStatus::Succeeded);
- },
- Err(error) => {
- self.compilation_status.set(ShaderCompilationStatus::Failed);
- debug!("Shader {} compilation failed: {}", self.id, error);
- },
- }
+ let source = self.source.borrow();
+
+ let mut params = BuiltInResources {
+ MaxVertexAttribs: limits.max_vertex_attribs as c_int,
+ MaxVertexUniformVectors: limits.max_vertex_uniform_vectors as c_int,
+ MaxVertexTextureImageUnits: limits.max_vertex_texture_image_units as c_int,
+ MaxCombinedTextureImageUnits: limits.max_combined_texture_image_units as c_int,
+ MaxTextureImageUnits: limits.max_texture_image_units as c_int,
+ MaxFragmentUniformVectors: limits.max_fragment_uniform_vectors as c_int,
+
+ MaxVertexOutputVectors: limits.max_vertex_output_vectors as c_int,
+ MaxFragmentInputVectors: limits.max_fragment_input_vectors as c_int,
+ MaxVaryingVectors: limits.max_varying_vectors as c_int,
+
+ OES_standard_derivatives: ext.is_enabled::<OESStandardDerivatives>() as c_int,
+ EXT_shader_texture_lod: ext.is_enabled::<EXTShaderTextureLod>() as c_int,
+ EXT_frag_depth: ext.is_enabled::<EXTFragDepth>() as c_int,
+
+ FragmentPrecisionHigh: 1,
+ ..default_validator()
+ };
+
+ if webgl_version == WebGLVersion::WebGL2 {
+ params.MinProgramTexelOffset = limits.min_program_texel_offset as c_int;
+ params.MaxProgramTexelOffset = limits.max_program_texel_offset as c_int;
+ params.MaxDrawBuffers = limits.max_draw_buffers as c_int;
+ }
- *self.info_log.borrow_mut() = Some(validator.info_log());
- // TODO(emilio): More data (like uniform data) should be collected
- // here to properly validate uniforms.
- //
- // This requires a more complex interface with ANGLE, using C++
- // bindings and being extremely cautious about destructing things.
+ let validator = match webgl_version {
+ WebGLVersion::WebGL1 => {
+ let output_format = if api_type == GlType::Gles {
+ Output::Essl
+ } else {
+ Output::Glsl
+ };
+ ShaderValidator::for_webgl(self.gl_type, output_format, &params).unwrap()
+ },
+ WebGLVersion::WebGL2 => {
+ let output_format = if api_type == GlType::Gles {
+ Output::Essl
+ } else {
+ match (glsl_version.major, glsl_version.minor) {
+ (1, 30) => Output::Glsl130,
+ (1, 40) => Output::Glsl140,
+ (1, 50) => Output::Glsl150Core,
+ (3, 30) => Output::Glsl330Core,
+ (4, 0) => Output::Glsl400Core,
+ (4, 10) => Output::Glsl410Core,
+ (4, 20) => Output::Glsl420Core,
+ (4, 30) => Output::Glsl430Core,
+ (4, 40) => Output::Glsl440Core,
+ (4, _) => Output::Glsl450Core,
+ _ => Output::Glsl140,
+ }
+ };
+ ShaderValidator::for_webgl2(self.gl_type, output_format, &params).unwrap()
+ },
+ };
+
+ // Replicating
+ // https://searchfox.org/mozilla-central/rev/c621276fbdd9591f52009042d959b9e19b66d49f/dom/canvas/WebGLShaderValidator.cpp#32
+ let options = mozangle::shaders::ffi::SH_VARIABLES |
+ mozangle::shaders::ffi::SH_ENFORCE_PACKING_RESTRICTIONS |
+ mozangle::shaders::ffi::SH_OBJECT_CODE |
+ mozangle::shaders::ffi::SH_INIT_GL_POSITION |
+ mozangle::shaders::ffi::SH_INITIALIZE_UNINITIALIZED_LOCALS |
+ mozangle::shaders::ffi::SH_INIT_OUTPUT_VARIABLES |
+ mozangle::shaders::ffi::SH_LIMIT_EXPRESSION_COMPLEXITY |
+ mozangle::shaders::ffi::SH_LIMIT_CALL_STACK_DEPTH |
+ if cfg!(target_os = "macos") {
+ // Work around https://bugs.webkit.org/show_bug.cgi?id=124684,
+ // https://chromium.googlesource.com/angle/angle/+/5e70cf9d0b1bb
+ mozangle::shaders::ffi::SH_UNFOLD_SHORT_CIRCUIT |
+ // Work around that Mac drivers handle struct scopes incorrectly.
+ mozangle::shaders::ffi::SH_REGENERATE_STRUCT_NAMES |
+ // Work around that Intel drivers on Mac OSX handle for-loop incorrectly.
+ mozangle::shaders::ffi::SH_ADD_AND_TRUE_TO_LOOP_CONDITION
+ } else {
+ // We want to do this everywhere, but to do this on Mac, we need
+ // to do it only on Mac OSX > 10.6 as this causes the shader
+ // compiler in 10.6 to crash
+ mozangle::shaders::ffi::SH_CLAMP_INDIRECT_ARRAY_BOUNDS
+ };
+
+ // Replicating
+ // https://github.com/servo/mozangle/blob/706a9baaf8026c1a3cb6c67ba63aa5f4734264d0/src/shaders/mod.rs#L226
+ let options = options |
+ mozangle::shaders::ffi::SH_VALIDATE |
+ mozangle::shaders::ffi::SH_OBJECT_CODE |
+ mozangle::shaders::ffi::SH_VARIABLES | // For uniform_name_map()
+ mozangle::shaders::ffi::SH_EMULATE_ABS_INT_FUNCTION | // To workaround drivers
+ mozangle::shaders::ffi::SH_EMULATE_ISNAN_FLOAT_FUNCTION | // To workaround drivers
+ mozangle::shaders::ffi::SH_EMULATE_ATAN2_FLOAT_FUNCTION | // To workaround drivers
+ mozangle::shaders::ffi::SH_CLAMP_INDIRECT_ARRAY_BOUNDS |
+ mozangle::shaders::ffi::SH_INIT_GL_POSITION |
+ mozangle::shaders::ffi::SH_ENFORCE_PACKING_RESTRICTIONS |
+ mozangle::shaders::ffi::SH_LIMIT_EXPRESSION_COMPLEXITY |
+ mozangle::shaders::ffi::SH_LIMIT_CALL_STACK_DEPTH;
+
+ match validator.compile(&[&source], options) {
+ Ok(()) => {
+ let translated_source = validator.object_code();
+ debug!("Shader translated: {}", translated_source);
+ // NOTE: At this point we should be pretty sure that the compilation in the paint thread
+ // will succeed.
+ // It could be interesting to retrieve the info log from the paint thread though
+ self.upcast::<WebGLObject>()
+ .context()
+ .send_command(WebGLCommand::CompileShader(self.id, translated_source));
+ self.compilation_status
+ .set(ShaderCompilationStatus::Succeeded);
+ },
+ Err(error) => {
+ self.compilation_status.set(ShaderCompilationStatus::Failed);
+ debug!("Shader {} compilation failed: {}", self.id, error);
+ },
}
+
+ *self.info_log.borrow_mut() = validator.info_log().into();
+
+ Ok(())
}
/// Mark this shader as deleted (if it wasn't previously)
/// and delete it as if calling glDeleteShader.
/// Currently does not check if shader is attached
- pub fn delete(&self) {
- if !self.is_deleted.get() {
- self.is_deleted.set(true);
- let _ = self.renderer.send(CanvasMsg::WebGL(WebGLCommand::DeleteShader(self.id)));
+ pub fn mark_for_deletion(&self, operation_fallibility: Operation) {
+ if !self.marked_for_deletion.get() {
+ self.marked_for_deletion.set(true);
+ let context = self.upcast::<WebGLObject>().context();
+ let cmd = WebGLCommand::DeleteShader(self.id);
+ match operation_fallibility {
+ Operation::Fallible => context.send_command_ignored(cmd),
+ Operation::Infallible => context.send_command(cmd),
+ }
}
}
+ pub fn is_marked_for_deletion(&self) -> bool {
+ self.marked_for_deletion.get()
+ }
+
pub fn is_deleted(&self) -> bool {
- self.is_deleted.get()
+ self.marked_for_deletion.get() && !self.is_attached()
}
pub fn is_attached(&self) -> bool {
@@ -164,25 +380,18 @@ impl WebGLShader {
}
/// glGetShaderInfoLog
- pub fn info_log(&self) -> Option<String> {
+ pub fn info_log(&self) -> DOMString {
self.info_log.borrow().clone()
}
- /// glGetParameter
- pub fn parameter(&self, param_id: u32) -> WebGLResult<WebGLParameter> {
- let (sender, receiver) = webrender_traits::channel::msg_channel().unwrap();
- self.renderer.send(CanvasMsg::WebGL(WebGLCommand::GetShaderParameter(self.id, param_id, sender))).unwrap();
- receiver.recv().unwrap()
- }
-
/// Get the shader source
- pub fn source(&self) -> Option<DOMString> {
+ pub fn source(&self) -> DOMString {
self.source.borrow().clone()
}
/// glShaderSource
pub fn set_source(&self, source: DOMString) {
- *self.source.borrow_mut() = Some(source);
+ *self.source.borrow_mut() = source;
}
pub fn successfully_compiled(&self) -> bool {
@@ -192,7 +401,6 @@ impl WebGLShader {
impl Drop for WebGLShader {
fn drop(&mut self) {
- assert!(self.attached_counter.get() == 0);
- self.delete();
+ self.mark_for_deletion(Operation::Fallible);
}
}