Bug 1556763 - Add layout qualifier to fragment shader output for advanced blend. r=kvark
authorJamie Nicol <jnicol@mozilla.com>
Sat, 15 Jun 2019 11:56:14 +0000
changeset 479032 501698e75525e076352bc02bfcb22a8b5ddf8d85
parent 479031 7d79dcab14bd5e5607f50fe2b489f30abf69d4b4
child 479033 6ac6838b4cd865dc2df270cea1c0255e1d9afe03
push id36157
push usershindli@mozilla.com
push dateSat, 15 Jun 2019 21:44:24 +0000
treeherdermozilla-central@5f48ef706159 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskvark
bugs1556763
milestone69.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1556763 - Add layout qualifier to fragment shader output for advanced blend. r=kvark When using an advanced blend equation, fragment shader output must be marked with a matching layout qualifier. Not doing so was causing subsequent glDraw* operations to fail. This patch adds a new shader feature, WR_FEATURE_ADVANCED_BLEND, which requires the necessary extension and adds the qualifier. Variants of the brush_image shaders are created with this feature, and are used whenever a brush_image shader is requested for BlendMode::Advanced. Differential Revision: https://phabricator.services.mozilla.com/D34617
gfx/wr/webrender/res/shared.glsl
gfx/wr/webrender/src/device/gl.rs
gfx/wr/webrender/src/renderer.rs
gfx/wr/webrender/src/shade.rs
--- a/gfx/wr/webrender/res/shared.glsl
+++ b/gfx/wr/webrender/res/shared.glsl
@@ -13,16 +13,20 @@
 #endif
 
 #ifdef WR_FEATURE_TEXTURE_EXTERNAL
 // Please check https://www.khronos.org/registry/OpenGL/extensions/OES/OES_EGL_image_external_essl3.txt
 // for this extension.
 #extension GL_OES_EGL_image_external_essl3 : require
 #endif
 
+#ifdef WR_FEATURE_ADVANCED_BLEND
+#extension GL_KHR_blend_equation_advanced : require
+#endif
+
 #ifdef WR_FEATURE_DUAL_SOURCE_BLENDING
 #ifdef GL_ES
 #extension GL_EXT_blend_func_extended : require
 #else
 #extension GL_ARB_explicit_attrib_location : require
 #endif
 #endif
 
@@ -102,16 +106,20 @@
         // Retrieve the current framebuffer color. Useful in conjunction with
         // the write_output_raw function.
         vec4 get_current_framebuffer_color() {
             return PLS.color;
         }
         #endif
     #else
         // Fragment shader outputs
+        #ifdef WR_FEATURE_ADVANCED_BLEND
+            layout(blend_support_all_equations) out;
+        #endif
+
         #ifdef WR_FEATURE_DUAL_SOURCE_BLENDING
             layout(location = 0, index = 0) out vec4 oFragColor;
             layout(location = 0, index = 1) out vec4 oFragBlend;
         #else
             out vec4 oFragColor;
         #endif
 
         // Write an output color in normal (non-PLS) shaders.
--- a/gfx/wr/webrender/src/device/gl.rs
+++ b/gfx/wr/webrender/src/device/gl.rs
@@ -898,16 +898,18 @@ pub struct Capabilities {
     /// Whether we are able to use glBlitFramebuffers with the draw fbo
     /// bound to a non-0th layer of a texture array. This is buggy on
     /// Adreno devices.
     pub supports_blit_to_texture_array: bool,
     /// Whether we can use the pixel local storage functionality that
     /// is available on some mobile GPUs. This allows fast access to
     /// the per-pixel tile memory.
     pub supports_pixel_local_storage: bool,
+    /// Whether advanced blend equations are supported.
+    pub supports_advanced_blend_equation: bool,
     /// Whether KHR_debug is supported for getting debug messages from
     /// the driver.
     pub supports_khr_debug: bool,
 }
 
 #[derive(Clone, Debug)]
 pub enum ShaderError {
     Compilation(String, String), // name, error message
@@ -1306,16 +1308,19 @@ impl Device {
         // TODO(gw): Support EXT_shader_framebuffer_fetch as well.
         let ext_pixel_local_storage = supports_extension(&extensions, "GL_EXT_shader_pixel_local_storage");
         let ext_framebuffer_fetch = supports_extension(&extensions, "GL_ARM_shader_framebuffer_fetch");
         let supports_pixel_local_storage =
             allow_pixel_local_storage_support &&
             ext_framebuffer_fetch &&
             ext_pixel_local_storage;
 
+        let supports_advanced_blend_equation =
+            supports_extension(&extensions, "GL_KHR_blend_equation_advanced");
+
         // On Adreno GPUs PBO texture upload is only performed asynchronously
         // if the stride of the data in the PBO is a multiple of 256 bytes.
         // Other platforms may have similar requirements and should be added
         // here.
         // The default value should be 4.
         let optimal_pbo_stride = if renderer_name.contains("Adreno") {
             NonZeroUsize::new(256).unwrap()
         } else {
@@ -1329,16 +1334,17 @@ impl Device {
             upload_method,
             inside_frame: false,
 
             capabilities: Capabilities {
                 supports_multisampling: false, //TODO
                 supports_copy_image_sub_data,
                 supports_blit_to_texture_array,
                 supports_pixel_local_storage,
+                supports_advanced_blend_equation,
                 supports_khr_debug,
             },
 
             bgra_format_internal,
             bgra_format_external,
 
             depth_targets: FastHashMap::default(),
 
--- a/gfx/wr/webrender/src/renderer.rs
+++ b/gfx/wr/webrender/src/renderer.rs
@@ -1788,17 +1788,17 @@ impl Renderer {
         let use_dual_source_blending =
             supports_dual_source_blending &&
             options.allow_dual_source_blending &&
             // If using pixel local storage, subpixel AA isn't supported (we disable it on all
             // mobile devices explicitly anyway).
             !device.get_capabilities().supports_pixel_local_storage;
         let ext_blend_equation_advanced =
             options.allow_advanced_blend_equation &&
-            device.supports_extension("GL_KHR_blend_equation_advanced");
+            device.get_capabilities().supports_advanced_blend_equation;
         let ext_blend_equation_advanced_coherent =
             device.supports_extension("GL_KHR_blend_equation_advanced_coherent");
 
         // 512 is the minimum that the texture cache can work with.
         const MIN_TEXTURE_SIZE: i32 = 512;
         if let Some(user_limit) = options.max_texture_size {
             assert!(user_limit >= MIN_TEXTURE_SIZE);
             device.clamp_max_texture_size(user_limit);
--- a/gfx/wr/webrender/src/shade.rs
+++ b/gfx/wr/webrender/src/shade.rs
@@ -42,16 +42,17 @@ impl ImageBufferKind {
 
 pub const IMAGE_BUFFER_KINDS: [ImageBufferKind; 4] = [
     ImageBufferKind::Texture2D,
     ImageBufferKind::TextureRect,
     ImageBufferKind::TextureExternal,
     ImageBufferKind::Texture2DArray,
 ];
 
+const ADVANCED_BLEND_FEATURE: &str = "ADVANCED_BLEND";
 const ALPHA_FEATURE: &str = "ALPHA_PASS";
 const DEBUG_OVERDRAW_FEATURE: &str = "DEBUG_OVERDRAW";
 const DITHERING_FEATURE: &str = "DITHERING";
 const DUAL_SOURCE_FEATURE: &str = "DUAL_SOURCE_BLENDING";
 const FAST_PATH_FEATURE: &str = "FAST_PATH";
 const PIXEL_LOCAL_STORAGE_FEATURE: &str = "PIXEL_LOCAL_STORAGE";
 
 pub(crate) enum ShaderKind {
@@ -253,26 +254,28 @@ impl LazilyCompiledShader {
 // alpha:
 //   Used for brush primitives in the alpha
 //   pass. Assumes that AA should be applied
 //   along the primitive edge, and also that
 //   clip mask is present.
 struct BrushShader {
     opaque: LazilyCompiledShader,
     alpha: LazilyCompiledShader,
+    advanced_blend: Option<LazilyCompiledShader>,
     dual_source: Option<LazilyCompiledShader>,
     debug_overdraw: LazilyCompiledShader,
 }
 
 impl BrushShader {
     fn new(
         name: &'static str,
         device: &mut Device,
         features: &[&'static str],
         precache_flags: ShaderPrecacheFlags,
+        advanced_blend: bool,
         dual_source: bool,
         use_pixel_local_storage: bool,
     ) -> Result<Self, ShaderError> {
         let opaque = LazilyCompiledShader::new(
             ShaderKind::Brush,
             name,
             features,
             device,
@@ -288,16 +291,35 @@ impl BrushShader {
         let alpha = LazilyCompiledShader::new(
             ShaderKind::Brush,
             name,
             &alpha_features,
             device,
             precache_flags,
         )?;
 
+        let advanced_blend = if advanced_blend &&
+            device.get_capabilities().supports_advanced_blend_equation
+        {
+            let mut advanced_blend_features = alpha_features.to_vec();
+            advanced_blend_features.push(ADVANCED_BLEND_FEATURE);
+
+            let shader = LazilyCompiledShader::new(
+                ShaderKind::Brush,
+                name,
+                &advanced_blend_features,
+                device,
+                precache_flags,
+            )?;
+
+            Some(shader)
+        } else {
+            None
+        };
+
         // If using PLS, we disable all subpixel AA implicitly. Subpixel AA is always
         // disabled on mobile devices anyway, due to uncertainty over the subpixel
         // layout configuration.
         let dual_source = if dual_source && !use_pixel_local_storage {
             let mut dual_source_features = alpha_features.to_vec();
             dual_source_features.push(DUAL_SOURCE_FEATURE);
 
             let shader = LazilyCompiledShader::new(
@@ -322,43 +344,51 @@ impl BrushShader {
             &debug_overdraw_features,
             device,
             precache_flags,
         )?;
 
         Ok(BrushShader {
             opaque,
             alpha,
+            advanced_blend,
             dual_source,
             debug_overdraw,
         })
     }
 
     fn get(&mut self, blend_mode: BlendMode, debug_flags: DebugFlags)
            -> &mut LazilyCompiledShader {
         match blend_mode {
             _ if debug_flags.contains(DebugFlags::SHOW_OVERDRAW) => &mut self.debug_overdraw,
             BlendMode::None => &mut self.opaque,
             BlendMode::Alpha |
             BlendMode::PremultipliedAlpha |
             BlendMode::PremultipliedDestOut |
             BlendMode::SubpixelConstantTextColor(..) |
-            BlendMode::SubpixelWithBgColor |
-            BlendMode::Advanced(_) => &mut self.alpha,
+            BlendMode::SubpixelWithBgColor => &mut self.alpha,
+            BlendMode::Advanced(_) => {
+                self.advanced_blend
+                    .as_mut()
+                    .expect("bug: no advanced blend shader loaded")
+            }
             BlendMode::SubpixelDualSource => {
                 self.dual_source
                     .as_mut()
                     .expect("bug: no dual source shader loaded")
             }
         }
     }
 
     fn deinit(self, device: &mut Device) {
         self.opaque.deinit(device);
         self.alpha.deinit(device);
+        if let Some(advanced_blend) = self.advanced_blend {
+            advanced_blend.deinit(device);
+        }
         if let Some(dual_source) = self.dual_source {
             dual_source.deinit(device);
         }
         self.debug_overdraw.deinit(device);
     }
 }
 
 pub struct TextShader {
@@ -529,61 +559,66 @@ impl Shaders {
             .get_capabilities()
             .supports_pixel_local_storage;
 
         let brush_solid = BrushShader::new(
             "brush_solid",
             device,
             &[],
             options.precache_flags,
-            false,
+            false /* advanced blend */,
+            false /* dual source */,
             use_pixel_local_storage,
         )?;
 
         let brush_blend = BrushShader::new(
             "brush_blend",
             device,
             &[],
             options.precache_flags,
-            false,
+            false /* advanced blend */,
+            false /* dual source */,
             use_pixel_local_storage,
         )?;
 
         let brush_mix_blend = BrushShader::new(
             "brush_mix_blend",
             device,
             &[],
             options.precache_flags,
-            false,
+            false /* advanced blend */,
+            false /* dual source */,
             use_pixel_local_storage,
         )?;
 
         let brush_radial_gradient = BrushShader::new(
             "brush_radial_gradient",
             device,
             if options.enable_dithering {
                &[DITHERING_FEATURE]
             } else {
                &[]
             },
             options.precache_flags,
-            false,
+            false /* advanced blend */,
+            false /* dual source */,
             use_pixel_local_storage,
         )?;
 
         let brush_linear_gradient = BrushShader::new(
             "brush_linear_gradient",
             device,
             if options.enable_dithering {
                &[DITHERING_FEATURE]
             } else {
                &[]
             },
             options.precache_flags,
-            false,
+            false /* advanced blend */,
+            false /* dual source */,
             use_pixel_local_storage,
         )?;
 
         let cs_blur_a8 = LazilyCompiledShader::new(
             ShaderKind::Cache(VertexArrayKind::Blur),
             "cs_blur",
             &["ALPHA_TARGET"],
             device,
@@ -715,16 +750,17 @@ impl Shaders {
                 if feature_string != "" {
                     image_features.push(feature_string);
                 }
                 brush_image[buffer_kind] = Some(BrushShader::new(
                     "brush_image",
                     device,
                     &image_features,
                     options.precache_flags,
+                    options.allow_advanced_blend_equation,
                     options.allow_dual_source_blending,
                     use_pixel_local_storage,
                 )?);
             }
             image_features.clear();
         }
 
         // All yuv_image configuration.
@@ -742,17 +778,18 @@ impl Shaders {
                     yuv_features.push(feature_string);
                 }
 
                 let shader = BrushShader::new(
                     "brush_yuv_image",
                     device,
                     &yuv_features,
                     options.precache_flags,
-                    false,
+                    false /* advanced blend */,
+                    false /* dual source */,
                     use_pixel_local_storage,
                 )?;
                 let index = Self::get_yuv_shader_index(
                     *image_buffer_kind,
                 );
                 brush_yuv_image[index] = Some(shader);
                 yuv_features.clear();
             }