Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  shade.rs   Sprache: unbekannt

 
/* 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/. */

use api::{ImageBufferKind, units::DeviceSize};
use crate::batch::{BatchKey, BatchKind, BrushBatchKind, BatchFeatures};
use crate::composite::{CompositeFeatures, CompositeSurfaceFormat};
use crate::device::{Device, Program, ShaderError};
use crate::pattern::PatternKind;
use euclid::default::Transform3D;
use glyph_rasterizer::GlyphFormat;
use crate::renderer::{
    desc,
    BlendMode, DebugFlags, RendererError, WebRenderOptions,
    TextureSampler, VertexArrayKind, ShaderPrecacheFlags,
};
use crate::profiler::{self, TransactionProfile, ns_to_ms};

use gleam::gl::GlType;
use time::precise_time_ns;

use std::cell::RefCell;
use std::rc::Rc;

use webrender_build::shader::{ShaderFeatures, ShaderFeatureFlags, get_shader_features};

/// Which extension version to use for texture external support.
#[derive(Clone, Copy, Debug, PartialEq)]
enum TextureExternalVersion {
    // GL_OES_EGL_image_external_essl3 (Compatible with ESSL 3.0 and
    // later shaders, but not supported on all GLES 3 devices.)
    ESSL3,
    // GL_OES_EGL_image_external (Compatible with ESSL 1.0 shaders)
    ESSL1,
}

fn get_feature_string(kind: ImageBufferKind, texture_external_version: TextureExternalVersion) -> &'static str {
    match (kind, texture_external_version) {
        (ImageBufferKind::Texture2D, _) => "TEXTURE_2D",
        (ImageBufferKind::TextureRect, _) => "TEXTURE_RECT",
        (ImageBufferKind::TextureExternal, TextureExternalVersion::ESSL3) => "TEXTURE_EXTERNAL",
        (ImageBufferKind::TextureExternal, TextureExternalVersion::ESSL1) => "TEXTURE_EXTERNAL_ESSL1",
        (ImageBufferKind::TextureExternalBT709, _) => "TEXTURE_EXTERNAL_BT709",
    }
}

fn has_platform_support(kind: ImageBufferKind, device: &Device) -> bool {
    match (kind, device.gl().get_type()) {
        (ImageBufferKind::Texture2D, _) => true,
        (ImageBufferKind::TextureRect, GlType::Gles) => false,
        (ImageBufferKind::TextureRect, GlType::Gl) => true,
        (ImageBufferKind::TextureExternal, GlType::Gles) => true,
        (ImageBufferKind::TextureExternal, GlType::Gl) => false,
        (ImageBufferKind::TextureExternalBT709, GlType::Gles) => device.supports_extension("GL_EXT_YUV_target"),
        (ImageBufferKind::TextureExternalBT709, GlType::Gl) => false,
    }
}

pub const IMAGE_BUFFER_KINDS: [ImageBufferKind; 4] = [
    ImageBufferKind::Texture2D,
    ImageBufferKind::TextureRect,
    ImageBufferKind::TextureExternal,
    ImageBufferKind::TextureExternalBT709,
];

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";

pub(crate) enum ShaderKind {
    Primitive,
    Cache(VertexArrayKind),
    ClipCache(VertexArrayKind),
    Brush,
    Text,
    #[allow(dead_code)]
    VectorStencil,
    #[allow(dead_code)]
    VectorCover,
    #[allow(dead_code)]
    Resolve,
    Composite,
    Clear,
    Copy,
}

pub struct LazilyCompiledShader {
    program: Option<Program>,
    name: &'static str,
    kind: ShaderKind,
    cached_projection: Transform3D<f32>,
    features: Vec<&'static str>,
}

impl LazilyCompiledShader {
    pub(crate) fn new(
        kind: ShaderKind,
        name: &'static str,
        unsorted_features: &[&'static str],
        device: &mut Device,
        precache_flags: ShaderPrecacheFlags,
        shader_list: &ShaderFeatures,
        profile: &mut TransactionProfile,
    ) -> Result<Self, ShaderError> {

        let mut features = unsorted_features.to_vec();
        features.sort();

        // Ensure this shader config is in the available shader list so that we get
        // alerted if the list gets out-of-date when shaders or features are added.
        let config = features.join(",");
        assert!(
            shader_list.get(name).map_or(false, |f| f.contains(&config)),
            "shader \"{}\" with features \"{}\" not in available shader list",
            name,
            config,
        );

        let mut shader = LazilyCompiledShader {
            program: None,
            name,
            kind,
            //Note: this isn't really the default state, but there is no chance
            // an actual projection passed here would accidentally match.
            cached_projection: Transform3D::identity(),
            features,
        };

        if precache_flags.intersects(ShaderPrecacheFlags::ASYNC_COMPILE | ShaderPrecacheFlags::FULL_COMPILE) {
            let t0 = precise_time_ns();
            shader.get_internal(device, precache_flags, profile)?;
            let t1 = precise_time_ns();
            debug!("[C: {:.1} ms ] Precache {} {:?}",
                (t1 - t0) as f64 / 1000000.0,
                name,
                unsorted_features
            );
        }

        Ok(shader)
    }

    pub fn bind(
        &mut self,
        device: &mut Device,
        projection: &Transform3D<f32>,
        texture_size: Option<DeviceSize>,
        renderer_errors: &mut Vec<RendererError>,
        profile: &mut TransactionProfile,
    ) {
        let update_projection = self.cached_projection != *projection;
        let program = match self.get_internal(device, ShaderPrecacheFlags::FULL_COMPILE, profile) {
            Ok(program) => program,
            Err(e) => {
                renderer_errors.push(RendererError::from(e));
                return;
            }
        };
        device.bind_program(program);
        if let Some(texture_size) = texture_size {
            device.set_shader_texture_size(program, texture_size);
        }
        if update_projection {
            device.set_uniforms(program, projection);
            // thanks NLL for this (`program` technically borrows `self`)
            self.cached_projection = *projection;
        }
    }

    fn get_internal(
        &mut self,
        device: &mut Device,
        precache_flags: ShaderPrecacheFlags,
        profile: &mut TransactionProfile,
    ) -> Result<&mut Program, ShaderError> {
        if self.program.is_none() {
            let start_time = precise_time_ns();
            let program = match self.kind {
                ShaderKind::Primitive | ShaderKind::Brush | ShaderKind::Text | ShaderKind::Resolve | ShaderKind::Clear | ShaderKind::Copy => {
                    create_prim_shader(
                        self.name,
                        device,
                        &self.features,
                    )
                }
                ShaderKind::Cache(..) => {
                    create_prim_shader(
                        self.name,
                        device,
                        &self.features,
                    )
                }
                ShaderKind::VectorStencil => {
                    create_prim_shader(
                        self.name,
                        device,
                        &self.features,
                    )
                }
                ShaderKind::VectorCover => {
                    create_prim_shader(
                        self.name,
                        device,
                        &self.features,
                    )
                }
                ShaderKind::Composite => {
                    create_prim_shader(
                        self.name,
                        device,
                        &self.features,
                    )
                }
                ShaderKind::ClipCache(..) => {
                    create_clip_shader(
                        self.name,
                        device,
                        &self.features,
                    )
                }
            };
            self.program = Some(program?);

            let end_time = precise_time_ns();
            profile.add(profiler::SHADER_BUILD_TIME, ns_to_ms(end_time - start_time));
        }

        let program = self.program.as_mut().unwrap();

        if precache_flags.contains(ShaderPrecacheFlags::FULL_COMPILE) && !program.is_initialized() {
            let start_time = precise_time_ns();

            let vertex_format = match self.kind {
                ShaderKind::Primitive |
                ShaderKind::Brush |
                ShaderKind::Text => VertexArrayKind::Primitive,
                ShaderKind::Cache(format) => format,
                ShaderKind::VectorStencil => VertexArrayKind::VectorStencil,
                ShaderKind::VectorCover => VertexArrayKind::VectorCover,
                ShaderKind::ClipCache(format) => format,
                ShaderKind::Resolve => VertexArrayKind::Resolve,
                ShaderKind::Composite => VertexArrayKind::Composite,
                ShaderKind::Clear => VertexArrayKind::Clear,
                ShaderKind::Copy => VertexArrayKind::Copy,
            };

            let vertex_descriptor = match vertex_format {
                VertexArrayKind::Primitive => &desc::PRIM_INSTANCES,
                VertexArrayKind::LineDecoration => &desc::LINE,
                VertexArrayKind::FastLinearGradient => &desc::FAST_LINEAR_GRADIENT,
                VertexArrayKind::LinearGradient => &desc::LINEAR_GRADIENT,
                VertexArrayKind::RadialGradient => &desc::RADIAL_GRADIENT,
                VertexArrayKind::ConicGradient => &desc::CONIC_GRADIENT,
                VertexArrayKind::Blur => &desc::BLUR,
                VertexArrayKind::ClipRect => &desc::CLIP_RECT,
                VertexArrayKind::ClipBoxShadow => &desc::CLIP_BOX_SHADOW,
                VertexArrayKind::VectorStencil => &desc::VECTOR_STENCIL,
                VertexArrayKind::VectorCover => &desc::VECTOR_COVER,
                VertexArrayKind::Border => &desc::BORDER,
                VertexArrayKind::Scale => &desc::SCALE,
                VertexArrayKind::Resolve => &desc::RESOLVE,
                VertexArrayKind::SvgFilter => &desc::SVG_FILTER,
                VertexArrayKind::SvgFilterNode => &desc::SVG_FILTER_NODE,
                VertexArrayKind::Composite => &desc::COMPOSITE,
                VertexArrayKind::Clear => &desc::CLEAR,
                VertexArrayKind::Copy => &desc::COPY,
                VertexArrayKind::Mask => &desc::MASK,
            };

            device.link_program(program, vertex_descriptor)?;
            device.bind_program(program);
            match self.kind {
                ShaderKind::ClipCache(..) => {
                    device.bind_shader_samplers(
                        &program,
                        &[
                            ("sColor0", TextureSampler::Color0),
                            ("sTransformPalette", TextureSampler::TransformPalette),
                            ("sRenderTasks", TextureSampler::RenderTasks),
                            ("sGpuCache", TextureSampler::GpuCache),
                            ("sPrimitiveHeadersF", TextureSampler::PrimitiveHeadersF),
                            ("sPrimitiveHeadersI", TextureSampler::PrimitiveHeadersI),
                            ("sGpuBufferF", TextureSampler::GpuBufferF),
                            ("sGpuBufferI", TextureSampler::GpuBufferI),
                        ],
                    );
                }
                _ => {
                    device.bind_shader_samplers(
                        &program,
                        &[
                            ("sColor0", TextureSampler::Color0),
                            ("sColor1", TextureSampler::Color1),
                            ("sColor2", TextureSampler::Color2),
                            ("sDither", TextureSampler::Dither),
                            ("sTransformPalette", TextureSampler::TransformPalette),
                            ("sRenderTasks", TextureSampler::RenderTasks),
                            ("sGpuCache", TextureSampler::GpuCache),
                            ("sPrimitiveHeadersF", TextureSampler::PrimitiveHeadersF),
                            ("sPrimitiveHeadersI", TextureSampler::PrimitiveHeadersI),
                            ("sClipMask", TextureSampler::ClipMask),
                            ("sGpuBufferF", TextureSampler::GpuBufferF),
                            ("sGpuBufferI", TextureSampler::GpuBufferI),
                        ],
                    );
                }
            }

            let end_time = precise_time_ns();
            profile.add(profiler::SHADER_BUILD_TIME, ns_to_ms(end_time - start_time));
        }

        Ok(program)
    }

    fn deinit(self, device: &mut Device) {
        if let Some(program) = self.program {
            device.delete_program(program);
        }
    }
}

// A brush shader supports two modes:
// opaque:
//   Used for completely opaque primitives,
//   or inside segments of partially
//   opaque primitives. Assumes no need
//   for clip masks, AA etc.
// 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,
        shader_list: &ShaderFeatures,
        use_advanced_blend: bool,
        use_dual_source: bool,
        profile: &mut TransactionProfile,
    ) -> Result<Self, ShaderError> {
        let opaque_features = features.to_vec();
        let opaque = LazilyCompiledShader::new(
            ShaderKind::Brush,
            name,
            &opaque_features,
            device,
            precache_flags,
            &shader_list,
            profile,
        )?;

        let mut alpha_features = opaque_features.to_vec();
        alpha_features.push(ALPHA_FEATURE);

        let alpha = LazilyCompiledShader::new(
            ShaderKind::Brush,
            name,
            &alpha_features,
            device,
            precache_flags,
            &shader_list,
            profile,
        )?;

        let advanced_blend = if use_advanced_blend {
            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,
                &shader_list,
                profile,
            )?;

            Some(shader)
        } else {
            None
        };

        let dual_source = if use_dual_source {
            let mut dual_source_features = alpha_features.to_vec();
            dual_source_features.push(DUAL_SOURCE_FEATURE);

            let shader = LazilyCompiledShader::new(
                ShaderKind::Brush,
                name,
                &dual_source_features,
                device,
                precache_flags,
                &shader_list,
                profile,
            )?;

            Some(shader)
        } else {
            None
        };

        let mut debug_overdraw_features = features.to_vec();
        debug_overdraw_features.push(DEBUG_OVERDRAW_FEATURE);

        let debug_overdraw = LazilyCompiledShader::new(
            ShaderKind::Brush,
            name,
            &debug_overdraw_features,
            device,
            precache_flags,
            &shader_list,
            profile,
        )?;

        Ok(BrushShader {
            opaque,
            alpha,
            advanced_blend,
            dual_source,
            debug_overdraw,
        })
    }

    fn get(&mut self, blend_mode: BlendMode, features: BatchFeatures, 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::Screen |
            BlendMode::PlusLighter |
            BlendMode::Exclusion => {
                if features.contains(BatchFeatures::ALPHA_PASS) {
                    &mut self.alpha
                } else {
                    &mut self.opaque
                }
            }
            BlendMode::Advanced(_) => {
                self.advanced_blend
                    .as_mut()
                    .expect("bug: no advanced blend shader loaded")
            }
            BlendMode::SubpixelDualSource |
            BlendMode::MultiplyDualSource => {
                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 {
    simple: LazilyCompiledShader,
    glyph_transform: LazilyCompiledShader,
    debug_overdraw: LazilyCompiledShader,
}

impl TextShader {
    fn new(
        name: &'static str,
        device: &mut Device,
        features: &[&'static str],
        precache_flags: ShaderPrecacheFlags,
        shader_list: &ShaderFeatures,
        profile: &mut TransactionProfile,
    ) -> Result<Self, ShaderError> {
        let mut simple_features = features.to_vec();
        simple_features.push("ALPHA_PASS");
        simple_features.push("TEXTURE_2D");

        let simple = LazilyCompiledShader::new(
            ShaderKind::Text,
            name,
            &simple_features,
            device,
            precache_flags,
            &shader_list,
            profile,
        )?;

        let mut glyph_transform_features = features.to_vec();
        glyph_transform_features.push("GLYPH_TRANSFORM");
        glyph_transform_features.push("ALPHA_PASS");
        glyph_transform_features.push("TEXTURE_2D");

        let glyph_transform = LazilyCompiledShader::new(
            ShaderKind::Text,
            name,
            &glyph_transform_features,
            device,
            precache_flags,
            &shader_list,
            profile,
        )?;

        let mut debug_overdraw_features = features.to_vec();
        debug_overdraw_features.push("DEBUG_OVERDRAW");
        debug_overdraw_features.push("TEXTURE_2D");

        let debug_overdraw = LazilyCompiledShader::new(
            ShaderKind::Text,
            name,
            &debug_overdraw_features,
            device,
            precache_flags,
            &shader_list,
            profile,
        )?;

        Ok(TextShader { simple, glyph_transform, debug_overdraw })
    }

    pub fn get(
        &mut self,
        glyph_format: GlyphFormat,
        debug_flags: DebugFlags,
    ) -> &mut LazilyCompiledShader {
        match glyph_format {
            _ if debug_flags.contains(DebugFlags::SHOW_OVERDRAW) => &mut self.debug_overdraw,
            GlyphFormat::Alpha |
            GlyphFormat::Subpixel |
            GlyphFormat::Bitmap |
            GlyphFormat::ColorBitmap => &mut self.simple,
            GlyphFormat::TransformedAlpha |
            GlyphFormat::TransformedSubpixel => &mut self.glyph_transform,
        }
    }

    fn deinit(self, device: &mut Device) {
        self.simple.deinit(device);
        self.glyph_transform.deinit(device);
        self.debug_overdraw.deinit(device);
    }
}

fn create_prim_shader(
    name: &'static str,
    device: &mut Device,
    features: &[&'static str],
) -> Result<Program, ShaderError> {
    debug!("PrimShader {}", name);

    device.create_program(name, features)
}

fn create_clip_shader(
    name: &'static str,
    device: &mut Device,
    features: &[&'static str],
) -> Result<Program, ShaderError> {
    debug!("ClipShader {}", name);

    device.create_program(name, features)
}

// NB: If you add a new shader here, make sure to deinitialize it
// in `Shaders::deinit()` below.
pub struct Shaders {
    // These are "cache shaders". These shaders are used to
    // draw intermediate results to cache targets. The results
    // of these shaders are then used by the primitive shaders.
    pub cs_blur_a8: LazilyCompiledShader,
    pub cs_blur_rgba8: LazilyCompiledShader,
    pub cs_border_segment: LazilyCompiledShader,
    pub cs_border_solid: LazilyCompiledShader,
    pub cs_scale: Vec<Option<LazilyCompiledShader>>,
    pub cs_line_decoration: LazilyCompiledShader,
    pub cs_fast_linear_gradient: LazilyCompiledShader,
    pub cs_linear_gradient: LazilyCompiledShader,
    pub cs_radial_gradient: LazilyCompiledShader,
    pub cs_conic_gradient: LazilyCompiledShader,
    pub cs_svg_filter: LazilyCompiledShader,
    pub cs_svg_filter_node: LazilyCompiledShader,

    // Brush shaders
    brush_solid: BrushShader,
    brush_image: Vec<Option<BrushShader>>,
    brush_fast_image: Vec<Option<BrushShader>>,
    brush_blend: BrushShader,
    brush_mix_blend: BrushShader,
    brush_yuv_image: Vec<Option<BrushShader>>,
    brush_linear_gradient: BrushShader,
    brush_opacity: BrushShader,
    brush_opacity_aa: BrushShader,

    /// These are "cache clip shaders". These shaders are used to
    /// draw clip instances into the cached clip mask. The results
    /// of these shaders are also used by the primitive shaders.
    pub cs_clip_rectangle_slow: LazilyCompiledShader,
    pub cs_clip_rectangle_fast: LazilyCompiledShader,
    pub cs_clip_box_shadow: LazilyCompiledShader,

    // The are "primitive shaders". These shaders draw and blend
    // final results on screen. They are aware of tile boundaries.
    // Most draw directly to the framebuffer, but some use inputs
    // from the cache shaders to draw. Specifically, the box
    // shadow primitive shader stretches the box shadow cache
    // output, and the cache_image shader blits the results of
    // a cache shader (e.g. blur) to the screen.
    pub ps_text_run: TextShader,
    pub ps_text_run_dual_source: Option<TextShader>,

    ps_split_composite: LazilyCompiledShader,
    pub ps_quad_textured: LazilyCompiledShader,
    pub ps_quad_radial_gradient: LazilyCompiledShader,
    pub ps_quad_conic_gradient: LazilyCompiledShader,
    pub ps_mask: LazilyCompiledShader,
    pub ps_mask_fast: LazilyCompiledShader,
    pub ps_clear: LazilyCompiledShader,
    pub ps_copy: LazilyCompiledShader,

    pub composite: CompositorShaders,
}

impl Shaders {
    pub fn new(
        device: &mut Device,
        gl_type: GlType,
        options: &WebRenderOptions,
    ) -> Result<Self, ShaderError> {
        // We have to pass a profile around a bunch but we aren't recording the initialization
        // so use a dummy one.
        let profile = &mut TransactionProfile::new();

        let use_dual_source_blending =
            device.get_capabilities().supports_dual_source_blending &&
            options.allow_dual_source_blending;
        let use_advanced_blend_equation =
            device.get_capabilities().supports_advanced_blend_equation &&
            options.allow_advanced_blend_equation;

        let texture_external_version = if device.get_capabilities().supports_image_external_essl3 {
            TextureExternalVersion::ESSL3
        } else {
            TextureExternalVersion::ESSL1
        };
        let mut shader_flags = get_shader_feature_flags(gl_type, texture_external_version, device);
        shader_flags.set(ShaderFeatureFlags::ADVANCED_BLEND_EQUATION, use_advanced_blend_equation);
        shader_flags.set(ShaderFeatureFlags::DUAL_SOURCE_BLENDING, use_dual_source_blending);
        shader_flags.set(ShaderFeatureFlags::DITHERING, options.enable_dithering);
        let shader_list = get_shader_features(shader_flags);

        let brush_solid = BrushShader::new(
            "brush_solid",
            device,
            &[],
            options.precache_flags,
            &shader_list,
            false /* advanced blend */,
            false /* dual source */,
            profile,
        )?;

        let brush_blend = BrushShader::new(
            "brush_blend",
            device,
            &[],
            options.precache_flags,
            &shader_list,
            false /* advanced blend */,
            false /* dual source */,
            profile,
        )?;

        let brush_mix_blend = BrushShader::new(
            "brush_mix_blend",
            device,
            &[],
            options.precache_flags,
            &shader_list,
            false /* advanced blend */,
            false /* dual source */,
            profile,
        )?;

        let brush_linear_gradient = BrushShader::new(
            "brush_linear_gradient",
            device,
            if options.enable_dithering {
               &[DITHERING_FEATURE]
            } else {
               &[]
            },
            options.precache_flags,
            &shader_list,
            false /* advanced blend */,
            false /* dual source */,
            profile,
        )?;

        let brush_opacity_aa = BrushShader::new(
            "brush_opacity",
            device,
            &["ANTIALIASING"],
            options.precache_flags,
            &shader_list,
            false /* advanced blend */,
            false /* dual source */,
            profile,
        )?;

        let brush_opacity = BrushShader::new(
            "brush_opacity",
            device,
            &[],
            options.precache_flags,
            &shader_list,
            false /* advanced blend */,
            false /* dual source */,
            profile,
        )?;

        let cs_blur_a8 = LazilyCompiledShader::new(
            ShaderKind::Cache(VertexArrayKind::Blur),
            "cs_blur",
            &["ALPHA_TARGET"],
            device,
            options.precache_flags,
            &shader_list,
            profile,
        )?;

        let cs_blur_rgba8 = LazilyCompiledShader::new(
            ShaderKind::Cache(VertexArrayKind::Blur),
            "cs_blur",
            &["COLOR_TARGET"],
            device,
            options.precache_flags,
            &shader_list,
            profile,
        )?;

        let cs_svg_filter = LazilyCompiledShader::new(
            ShaderKind::Cache(VertexArrayKind::SvgFilter),
            "cs_svg_filter",
            &[],
            device,
            options.precache_flags,
            &shader_list,
            profile,
        )?;

        let cs_svg_filter_node = LazilyCompiledShader::new(
            ShaderKind::Cache(VertexArrayKind::SvgFilterNode),
            "cs_svg_filter_node",
            &[],
            device,
            options.precache_flags,
            &shader_list,
            profile,
        )?;

        let ps_mask = LazilyCompiledShader::new(
            ShaderKind::Cache(VertexArrayKind::Mask),
            "ps_quad_mask",
            &[],
            device,
            options.precache_flags,
            &shader_list,
            profile,
        )?;

        let ps_mask_fast = LazilyCompiledShader::new(
            ShaderKind::Cache(VertexArrayKind::Mask),
            "ps_quad_mask",
            &[FAST_PATH_FEATURE],
            device,
            options.precache_flags,
            &shader_list,
            profile,
        )?;

        let cs_clip_rectangle_slow = LazilyCompiledShader::new(
            ShaderKind::ClipCache(VertexArrayKind::ClipRect),
            "cs_clip_rectangle",
            &[],
            device,
            options.precache_flags,
            &shader_list,
            profile,
        )?;

        let cs_clip_rectangle_fast = LazilyCompiledShader::new(
            ShaderKind::ClipCache(VertexArrayKind::ClipRect),
            "cs_clip_rectangle",
            &[FAST_PATH_FEATURE],
            device,
            options.precache_flags,
            &shader_list,
            profile,
        )?;

        let cs_clip_box_shadow = LazilyCompiledShader::new(
            ShaderKind::ClipCache(VertexArrayKind::ClipBoxShadow),
            "cs_clip_box_shadow",
            &["TEXTURE_2D"],
            device,
            options.precache_flags,
            &shader_list,
            profile,
        )?;

        let mut cs_scale = Vec::new();
        let scale_shader_num = IMAGE_BUFFER_KINDS.len();
        // PrimitiveShader is not clonable. Use push() to initialize the vec.
        for _ in 0 .. scale_shader_num {
            cs_scale.push(None);
        }
        for image_buffer_kind in &IMAGE_BUFFER_KINDS {
            if has_platform_support(*image_buffer_kind, device) {
                let feature_string = get_feature_string(
                    *image_buffer_kind,
                    texture_external_version,
                );

                let mut features = Vec::new();
                if feature_string != "" {
                    features.push(feature_string);
                }

                let shader = LazilyCompiledShader::new(
                    ShaderKind::Cache(VertexArrayKind::Scale),
                    "cs_scale",
                    &features,
                    device,
                    options.precache_flags,
                    &shader_list,
                    profile,
                 )?;

                 let index = Self::get_compositing_shader_index(
                    *image_buffer_kind,
                 );
                 cs_scale[index] = Some(shader);
            }
        }

        // TODO(gw): The split composite + text shader are special cases - the only
        //           shaders used during normal scene rendering that aren't a brush
        //           shader. Perhaps we can unify these in future?

        let ps_text_run = TextShader::new("ps_text_run",
            device,
            &[],
            options.precache_flags,
            &shader_list,
            profile,
        )?;

        let ps_text_run_dual_source = if use_dual_source_blending {
            let dual_source_features = vec![DUAL_SOURCE_FEATURE];
            Some(TextShader::new("ps_text_run",
                device,
                &dual_source_features,
                options.precache_flags,
                &shader_list,
                profile,
            )?)
        } else {
            None
        };

        let ps_quad_textured = LazilyCompiledShader::new(
            ShaderKind::Primitive,
            "ps_quad_textured",
            &[],
            device,
            options.precache_flags,
            &shader_list,
            profile,
        )?;

        let ps_quad_radial_gradient = LazilyCompiledShader::new(
            ShaderKind::Primitive,
            "ps_quad_radial_gradient",
            &[],
            device,
            options.precache_flags,
            &shader_list,
            profile,
        )?;

        let ps_quad_conic_gradient = LazilyCompiledShader::new(
            ShaderKind::Primitive,
            "ps_quad_conic_gradient",
            &[],
            device,
            options.precache_flags,
            &shader_list,
            profile,
        )?;

        let ps_split_composite = LazilyCompiledShader::new(
            ShaderKind::Primitive,
            "ps_split_composite",
            &[],
            device,
            options.precache_flags,
            &shader_list,
            profile,
        )?;

        let ps_clear = LazilyCompiledShader::new(
            ShaderKind::Clear,
            "ps_clear",
            &[],
            device,
            options.precache_flags,
            &shader_list,
            profile,
        )?;

        let ps_copy = LazilyCompiledShader::new(
            ShaderKind::Copy,
            "ps_copy",
            &[],
            device,
            options.precache_flags,
            &shader_list,
            profile,
        )?;

        // All image configuration.
        let mut image_features = Vec::new();
        let mut brush_image = Vec::new();
        let mut brush_fast_image = Vec::new();
        // PrimitiveShader is not clonable. Use push() to initialize the vec.
        for _ in 0 .. IMAGE_BUFFER_KINDS.len() {
            brush_image.push(None);
            brush_fast_image.push(None);
        }
        for buffer_kind in 0 .. IMAGE_BUFFER_KINDS.len() {
            if !has_platform_support(IMAGE_BUFFER_KINDS[buffer_kind], device)
                // Brush shaders are not ESSL1 compatible
                || (IMAGE_BUFFER_KINDS[buffer_kind] == ImageBufferKind::TextureExternal
                    && texture_external_version == TextureExternalVersion::ESSL1)
            {
                continue;
            }

            let feature_string = get_feature_string(
                IMAGE_BUFFER_KINDS[buffer_kind],
                texture_external_version,
            );
            if feature_string != "" {
                image_features.push(feature_string);
            }

            brush_fast_image[buffer_kind] = Some(BrushShader::new(
                "brush_image",
                device,
                &image_features,
                options.precache_flags,
                &shader_list,
                use_advanced_blend_equation,
                use_dual_source_blending,
                profile,
            )?);

            image_features.push("REPETITION");
            image_features.push("ANTIALIASING");

            brush_image[buffer_kind] = Some(BrushShader::new(
                "brush_image",
                device,
                &image_features,
                options.precache_flags,
                &shader_list,
                use_advanced_blend_equation,
                use_dual_source_blending,
                profile,
            )?);

            image_features.clear();
        }

        // All yuv_image configuration.
        let mut yuv_features = Vec::new();
        let mut rgba_features = Vec::new();
        let mut fast_path_features = Vec::new();
        let yuv_shader_num = IMAGE_BUFFER_KINDS.len();
        let mut brush_yuv_image = Vec::new();
        // PrimitiveShader is not clonable. Use push() to initialize the vec.
        for _ in 0 .. yuv_shader_num {
            brush_yuv_image.push(None);
        }
        for image_buffer_kind in &IMAGE_BUFFER_KINDS {
            if has_platform_support(*image_buffer_kind, device) {
                yuv_features.push("YUV");
                fast_path_features.push("FAST_PATH");

                let index = Self::get_compositing_shader_index(
                    *image_buffer_kind,
                );

                let feature_string = get_feature_string(
                    *image_buffer_kind,
                    texture_external_version,
                );
                if feature_string != "" {
                    yuv_features.push(feature_string);
                    rgba_features.push(feature_string);
                    fast_path_features.push(feature_string);
                }

                // YUV shaders are not compatible with ESSL1
                if *image_buffer_kind != ImageBufferKind::TextureExternal ||
                    texture_external_version == TextureExternalVersion::ESSL3 {
                    let brush_shader = BrushShader::new(
                        "brush_yuv_image",
                        device,
                        &yuv_features,
                        options.precache_flags,
                        &shader_list,
                        false /* advanced blend */,
                        false /* dual source */,
                        profile,
                    )?;
                    brush_yuv_image[index] = Some(brush_shader);
                }

                yuv_features.clear();
                rgba_features.clear();
                fast_path_features.clear();
            }
        }

        let cs_line_decoration = LazilyCompiledShader::new(
            ShaderKind::Cache(VertexArrayKind::LineDecoration),
            "cs_line_decoration",
            &[],
            device,
            options.precache_flags,
            &shader_list,
            profile,
        )?;

        let cs_fast_linear_gradient = LazilyCompiledShader::new(
            ShaderKind::Cache(VertexArrayKind::FastLinearGradient),
            "cs_fast_linear_gradient",
            &[],
            device,
            options.precache_flags,
            &shader_list,
            profile,
        )?;

        let cs_linear_gradient = LazilyCompiledShader::new(
            ShaderKind::Cache(VertexArrayKind::LinearGradient),
            "cs_linear_gradient",
            &[],
            device,
            options.precache_flags,
            &shader_list,
            profile,
        )?;

        let cs_radial_gradient = LazilyCompiledShader::new(
            ShaderKind::Cache(VertexArrayKind::RadialGradient),
            "cs_radial_gradient",
            &[],
            device,
            options.precache_flags,
            &shader_list,
            profile,
        )?;

        let cs_conic_gradient = LazilyCompiledShader::new(
            ShaderKind::Cache(VertexArrayKind::ConicGradient),
            "cs_conic_gradient",
            &[],
            device,
            options.precache_flags,
            &shader_list,
            profile,
        )?;

        let cs_border_segment = LazilyCompiledShader::new(
            ShaderKind::Cache(VertexArrayKind::Border),
            "cs_border_segment",
             &[],
             device,
             options.precache_flags,
            &shader_list,
            profile,
        )?;

        let cs_border_solid = LazilyCompiledShader::new(
            ShaderKind::Cache(VertexArrayKind::Border),
            "cs_border_solid",
            &[],
            device,
            options.precache_flags,
            &shader_list,
            profile,
        )?;

        let composite = CompositorShaders::new(device, options.precache_flags, gl_type)?;

        Ok(Shaders {
            cs_blur_a8,
            cs_blur_rgba8,
            cs_border_segment,
            cs_line_decoration,
            cs_fast_linear_gradient,
            cs_linear_gradient,
            cs_radial_gradient,
            cs_conic_gradient,
            cs_border_solid,
            cs_scale,
            cs_svg_filter,
            cs_svg_filter_node,
            brush_solid,
            brush_image,
            brush_fast_image,
            brush_blend,
            brush_mix_blend,
            brush_yuv_image,
            brush_linear_gradient,
            brush_opacity,
            brush_opacity_aa,
            cs_clip_rectangle_slow,
            cs_clip_rectangle_fast,
            cs_clip_box_shadow,
            ps_text_run,
            ps_text_run_dual_source,
            ps_quad_textured,
            ps_quad_radial_gradient,
            ps_quad_conic_gradient,
            ps_mask,
            ps_mask_fast,
            ps_split_composite,
            ps_clear,
            ps_copy,
            composite,
        })
    }

    fn get_compositing_shader_index(buffer_kind: ImageBufferKind) -> usize {
        buffer_kind as usize
    }

    pub fn get_composite_shader(
        &mut self,
        format: CompositeSurfaceFormat,
        buffer_kind: ImageBufferKind,
        features: CompositeFeatures,
    ) -> &mut LazilyCompiledShader {
        self.composite.get(format, buffer_kind, features)
    }

    pub fn get_scale_shader(
        &mut self,
        buffer_kind: ImageBufferKind,
    ) -> &mut LazilyCompiledShader {
        let shader_index = Self::get_compositing_shader_index(buffer_kind);
        self.cs_scale[shader_index]
            .as_mut()
            .expect("bug: unsupported scale shader requested")
    }

    pub fn get_quad_shader(
        &mut self,
        pattern: PatternKind
    ) -> &mut LazilyCompiledShader {
        match pattern {
            PatternKind::ColorOrTexture => &mut self.ps_quad_textured,
            PatternKind::RadialGradient => &mut self.ps_quad_radial_gradient,
            PatternKind::ConicGradient => &mut self.ps_quad_conic_gradient,
            PatternKind::Mask => unreachable!(),
        }
    }

    pub fn get(&
        mut self,
        key: &BatchKey,
        mut features: BatchFeatures,
        debug_flags: DebugFlags,
        device: &Device,
    ) -> &mut LazilyCompiledShader {
        match key.kind {
            BatchKind::Quad(PatternKind::ColorOrTexture) => {
                &mut self.ps_quad_textured
            }
            BatchKind::Quad(PatternKind::RadialGradient) => {
                &mut self.ps_quad_radial_gradient
            }
            BatchKind::Quad(PatternKind::ConicGradient) => {
                &mut self.ps_quad_conic_gradient
            }
            BatchKind::Quad(PatternKind::Mask) => {
                unreachable!();
            }
            BatchKind::SplitComposite => {
                &mut self.ps_split_composite
            }
            BatchKind::Brush(brush_kind) => {
                // SWGL uses a native anti-aliasing implementation that bypasses the shader.
                // Don't consider it in that case when deciding whether or not to use
                // an alpha-pass shader.
                if device.get_capabilities().uses_native_antialiasing {
                    features.remove(BatchFeatures::ANTIALIASING);
                }
                let brush_shader = match brush_kind {
                    BrushBatchKind::Solid => {
                        &mut self.brush_solid
                    }
                    BrushBatchKind::Image(image_buffer_kind) => {
                        if features.contains(BatchFeatures::ANTIALIASING) ||
                            features.contains(BatchFeatures::REPETITION) {

                            self.brush_image[image_buffer_kind as usize]
                                .as_mut()
                                .expect("Unsupported image shader kind")
                        } else {
                            self.brush_fast_image[image_buffer_kind as usize]
                                .as_mut()
                                .expect("Unsupported image shader kind")
                        }
                    }
                    BrushBatchKind::Blend => {
                        &mut self.brush_blend
                    }
                    BrushBatchKind::MixBlend { .. } => {
                        &mut self.brush_mix_blend
                    }
                    BrushBatchKind::LinearGradient => {
                        // SWGL uses a native clip mask implementation that bypasses the shader.
                        // Don't consider it in that case when deciding whether or not to use
                        // an alpha-pass shader.
                        if device.get_capabilities().uses_native_clip_mask {
                            features.remove(BatchFeatures::CLIP_MASK);
                        }
                        // Gradient brushes can optimistically use the opaque shader even
                        // with a blend mode if they don't require any features.
                        if !features.intersects(
                            BatchFeatures::ANTIALIASING
                                | BatchFeatures::REPETITION
                                | BatchFeatures::CLIP_MASK,
                        ) {
                            features.remove(BatchFeatures::ALPHA_PASS);
                        }
                        match brush_kind {
                            BrushBatchKind::LinearGradient => &mut self.brush_linear_gradient,
                            _ => panic!(),
                        }
                    }
                    BrushBatchKind::YuvImage(image_buffer_kind, ..) => {
                        let shader_index =
                            Self::get_compositing_shader_index(image_buffer_kind);
                        self.brush_yuv_image[shader_index]
                            .as_mut()
                            .expect("Unsupported YUV shader kind")
                    }
                    BrushBatchKind::Opacity => {
                        if features.contains(BatchFeatures::ANTIALIASING) {
                            &mut self.brush_opacity_aa
                        } else {
                            &mut self.brush_opacity
                        }
                    }
                };
                brush_shader.get(key.blend_mode, features, debug_flags)
            }
            BatchKind::TextRun(glyph_format) => {
                let text_shader = match key.blend_mode {
                    BlendMode::SubpixelDualSource => self.ps_text_run_dual_source.as_mut().unwrap(),
                    _ => &mut self.ps_text_run,
                };
                text_shader.get(glyph_format, debug_flags)
            }
        }
    }

    pub fn deinit(mut self, device: &mut Device) {
        for shader in self.cs_scale {
            if let Some(shader) = shader {
                shader.deinit(device);
            }
        }
        self.cs_blur_a8.deinit(device);
        self.cs_blur_rgba8.deinit(device);
        self.cs_svg_filter.deinit(device);
        self.cs_svg_filter_node.deinit(device);
        self.brush_solid.deinit(device);
        self.brush_blend.deinit(device);
        self.brush_mix_blend.deinit(device);
        self.brush_linear_gradient.deinit(device);
        self.brush_opacity.deinit(device);
        self.brush_opacity_aa.deinit(device);
        self.cs_clip_rectangle_slow.deinit(device);
        self.cs_clip_rectangle_fast.deinit(device);
        self.cs_clip_box_shadow.deinit(device);
        self.ps_text_run.deinit(device);
        if let Some(shader) = self.ps_text_run_dual_source {
            shader.deinit(device);
        }
        for shader in self.brush_image {
            if let Some(shader) = shader {
                shader.deinit(device);
            }
        }
        for shader in self.brush_fast_image {
            if let Some(shader) = shader {
                shader.deinit(device);
            }
        }
        for shader in self.brush_yuv_image {
            if let Some(shader) = shader {
                shader.deinit(device);
            }
        }
        self.cs_border_solid.deinit(device);
        self.cs_fast_linear_gradient.deinit(device);
        self.cs_linear_gradient.deinit(device);
        self.cs_radial_gradient.deinit(device);
        self.cs_conic_gradient.deinit(device);
        self.cs_line_decoration.deinit(device);
        self.cs_border_segment.deinit(device);
        self.ps_split_composite.deinit(device);
        self.ps_quad_textured.deinit(device);
        self.ps_quad_radial_gradient.deinit(device);
        self.ps_quad_conic_gradient.deinit(device);
        self.ps_mask.deinit(device);
        self.ps_mask_fast.deinit(device);
        self.ps_clear.deinit(device);
        self.ps_copy.deinit(device);
        self.composite.deinit(device);
    }
}

pub type SharedShaders = Rc<RefCell<Shaders>>;

pub struct CompositorShaders {
    // Composite shaders. These are very simple shaders used to composite
    // picture cache tiles into the framebuffer on platforms that do not have an
    // OS Compositor (or we cannot use it).  Such an OS Compositor (such as
    // DirectComposite or CoreAnimation) handles the composition of the picture
    // cache tiles at a lower level (e.g. in DWM for Windows); in that case we
    // directly hand the picture cache surfaces over to the OS Compositor, and
    // our own Composite shaders below never run.
    // To composite external (RGB) surfaces we need various permutations of
    // shaders with WR_FEATURE flags on or off based on the type of image
    // buffer we're sourcing from (see IMAGE_BUFFER_KINDS).
    rgba: Vec<Option<LazilyCompiledShader>>,
    // A faster set of rgba composite shaders that do not support UV clamping
    // or color modulation.
    rgba_fast_path: Vec<Option<LazilyCompiledShader>>,
    // The same set of composite shaders but with WR_FEATURE_YUV added.
    yuv: Vec<Option<LazilyCompiledShader>>,
}

impl CompositorShaders {
    pub fn new(
        device: &mut Device,
        precache_flags: ShaderPrecacheFlags,
        gl_type: GlType,
    )  -> Result<Self, ShaderError>  {
        // We have to pass a profile around a bunch but we aren't recording the initialization
        // so use a dummy one.
        let mut profile = TransactionProfile::new();

        let mut yuv_features = Vec::new();
        let mut rgba_features = Vec::new();
        let mut fast_path_features = Vec::new();
        let mut rgba = Vec::new();
        let mut rgba_fast_path = Vec::new();
        let mut yuv = Vec::new();

        let texture_external_version = if device.get_capabilities().supports_image_external_essl3 {
            TextureExternalVersion::ESSL3
        } else {
            TextureExternalVersion::ESSL1
        };

        let feature_flags = get_shader_feature_flags(gl_type, texture_external_version, device);
        let shader_list = get_shader_features(feature_flags);

        for _ in 0..IMAGE_BUFFER_KINDS.len() {
            yuv.push(None);
            rgba.push(None);
            rgba_fast_path.push(None);
        }

        for image_buffer_kind in &IMAGE_BUFFER_KINDS {
            if !has_platform_support(*image_buffer_kind, device) {
                continue;
            }

            yuv_features.push("YUV");
            fast_path_features.push("FAST_PATH");
    
            let index = Self::get_shader_index(*image_buffer_kind);

            let feature_string = get_feature_string(
                *image_buffer_kind,
                texture_external_version,
            );
            if feature_string != "" {
                yuv_features.push(feature_string);
                rgba_features.push(feature_string);
                fast_path_features.push(feature_string);
            }

            // YUV shaders are not compatible with ESSL1
            if *image_buffer_kind != ImageBufferKind::TextureExternal ||
                texture_external_version == TextureExternalVersion::ESSL3 {

                yuv[index] = Some(LazilyCompiledShader::new(
                    ShaderKind::Composite,
                    "composite",
                    &yuv_features,
                    device,
                    precache_flags,
                    &shader_list,
                    &mut profile,
                )?);
            }

            rgba[index] = Some(LazilyCompiledShader::new(
                ShaderKind::Composite,
                "composite",
                &rgba_features,
                device,
                precache_flags,
                &shader_list,
                &mut profile,
            )?);

            rgba_fast_path[index] = Some(LazilyCompiledShader::new(
                ShaderKind::Composite,
                "composite",
                &fast_path_features,
                device,
                precache_flags,
                &shader_list,
                &mut profile,
            )?);

            yuv_features.clear();
            rgba_features.clear();
            fast_path_features.clear();
        }

        Ok(CompositorShaders {
            rgba,
            rgba_fast_path,
            yuv,
        })
    }

    pub fn get(
        &mut self,
        format: CompositeSurfaceFormat,
        buffer_kind: ImageBufferKind,
        features: CompositeFeatures,
    ) -> &mut LazilyCompiledShader {
        match format {
            CompositeSurfaceFormat::Rgba => {
                if features.contains(CompositeFeatures::NO_UV_CLAMP)
                    && features.contains(CompositeFeatures::NO_COLOR_MODULATION)
                {
                    let shader_index = Self::get_shader_index(buffer_kind);
                    self.rgba_fast_path[shader_index]
                        .as_mut()
                        .expect("bug: unsupported rgba fast path shader requested")
                } else {
                    let shader_index = Self::get_shader_index(buffer_kind);
                    self.rgba[shader_index]
                        .as_mut()
                        .expect("bug: unsupported rgba shader requested")
                }
            }
            CompositeSurfaceFormat::Yuv => {
                let shader_index = Self::get_shader_index(buffer_kind);
                self.yuv[shader_index]
                    .as_mut()
                    .expect("bug: unsupported yuv shader requested")
            }
        }
    }

    fn get_shader_index(buffer_kind: ImageBufferKind) -> usize {
        buffer_kind as usize
    }

    pub fn deinit(&mut self, device: &mut Device) {
        for shader in self.rgba.drain(..) {
            if let Some(shader) = shader {
                shader.deinit(device);
            }
        }
        for shader in self.rgba_fast_path.drain(..) {
            if let Some(shader) = shader {
                shader.deinit(device);
            }
        }
        for shader in self.yuv.drain(..) {
            if let Some(shader) = shader {
                shader.deinit(device);
            }
        }
    }
}

fn get_shader_feature_flags(
    gl_type: GlType,
    texture_external_version: TextureExternalVersion,
    device: &Device
) -> ShaderFeatureFlags {
    match gl_type {
        GlType::Gl => ShaderFeatureFlags::GL,
        GlType::Gles => {
            let mut flags = ShaderFeatureFlags::GLES;
            flags |= match texture_external_version {
                TextureExternalVersion::ESSL3 => ShaderFeatureFlags::TEXTURE_EXTERNAL,
                TextureExternalVersion::ESSL1 => ShaderFeatureFlags::TEXTURE_EXTERNAL_ESSL1,
            };
            if device.supports_extension("GL_EXT_YUV_target") {
                flags |= ShaderFeatureFlags::TEXTURE_EXTERNAL_BT709;
            }
            flags
        }
    }
}

[ Dauer der Verarbeitung: 0.9 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge