diff --git a/Cargo.lock b/Cargo.lock index 21e269c4af..7758842697 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1542,6 +1542,7 @@ dependencies = [ "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", "gleam 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "glsl-to-cxx 0.1.0", + "webrender_build 0.0.1", ] [[package]] @@ -1868,6 +1869,7 @@ dependencies = [ name = "webrender_build" version = "0.0.1" dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/glsl-to-cxx/src/hir.rs b/glsl-to-cxx/src/hir.rs index f668c585bb..e1511e613d 100644 --- a/glsl-to-cxx/src/hir.rs +++ b/glsl-to-cxx/src/hir.rs @@ -3164,6 +3164,9 @@ pub fn ast_to_hir(state: &mut State, tu: &syntax::TranslationUnit) -> Translatio ); declare_function(state, "cos", None, Type::new(Float), vec![Type::new(Float)]); declare_function(state, "sin", None, Type::new(Float), vec![Type::new(Float)]); + declare_function(state, "tan", None, Type::new(Float), vec![Type::new(Float)]); + declare_function(state, "atan", None, Type::new(Float), vec![Type::new(Float)]); + declare_function(state, "atan", None, Type::new(Float), vec![Type::new(Float), Type::new(Float)]); declare_function( state, "clamp", @@ -3374,14 +3377,14 @@ pub fn ast_to_hir(state: &mut State, tu: &syntax::TranslationUnit) -> Translatio "texture", None, Type::new(Vec4), - vec![Type::new(Sampler2D), Type::new(Vec3)], + vec![Type::new(Sampler2D), Type::new(Vec2)], ); declare_function( state, "texture", None, Type::new(Vec4), - vec![Type::new(Sampler2D), Type::new(Vec2)], + vec![Type::new(Sampler2DRect), Type::new(Vec2)], ); declare_function( state, @@ -3411,6 +3414,13 @@ pub fn ast_to_hir(state: &mut State, tu: &syntax::TranslationUnit) -> Translatio Type::new(IVec2), vec![Type::new(Sampler2D), Type::new(Int)], ); + declare_function( + state, + "textureSize", + None, + Type::new(IVec2), + vec![Type::new(Sampler2DRect), Type::new(Int)], + ); declare_function( state, diff --git a/glsl-to-cxx/src/lib.rs b/glsl-to-cxx/src/lib.rs index 7ce461abf0..a4b2873380 100644 --- a/glsl-to-cxx/src/lib.rs +++ b/glsl-to-cxx/src/lib.rs @@ -319,6 +319,7 @@ fn write_program_samplers(state: &mut OutputState, uniform_indices: &UniformIndi for (name, (_, tk, storage)) in uniform_indices.iter() { match tk { hir::TypeKind::Sampler2D + | hir::TypeKind::Sampler2DRect | hir::TypeKind::ISampler2D | hir::TypeKind::Sampler2DArray => { write!(state, " "); @@ -344,6 +345,7 @@ fn write_program_samplers(state: &mut OutputState, uniform_indices: &UniformIndi for (name, (index, tk, _)) in uniform_indices.iter() { match tk { hir::TypeKind::Sampler2D + | hir::TypeKind::Sampler2DRect | hir::TypeKind::ISampler2D | hir::TypeKind::Sampler2DArray => { write!(state, " case {}:\n", index); @@ -370,7 +372,8 @@ fn write_bind_textures(state: &mut OutputState, uniforms: &[hir::SymRef]) { hir::SymDecl::Global(hir::StorageClass::Sampler(_format), _, ty, _) => { let name = sym.name.as_str(); match ty.kind { - hir::TypeKind::Sampler2D => write!(state, + hir::TypeKind::Sampler2D + | hir::TypeKind::Sampler2DRect => write!(state, " self->{0} = lookup_sampler(&prog->samplers.{0}_impl, prog->samplers.{0}_slot);\n", name), hir::TypeKind::ISampler2D => write!(state, @@ -2678,7 +2681,8 @@ fn define_texel_fetch_ptr( show_indent(state); if let hir::SymDecl::Global(_, _, ty, _) = &sampler_sym.decl { match ty.kind { - hir::TypeKind::Sampler2D => { + hir::TypeKind::Sampler2D + | hir::TypeKind::Sampler2DRect => { write!( state, "vec4_scalar* {}_{}_fetch = ", diff --git a/swgl/Cargo.toml b/swgl/Cargo.toml index 55df40ccba..eaed858855 100644 --- a/swgl/Cargo.toml +++ b/swgl/Cargo.toml @@ -9,6 +9,7 @@ description = "Software OpenGL implementation for WebRender." [build-dependencies] cc = "1.0.46" glsl-to-cxx = { path = "../glsl-to-cxx" } +webrender_build = { path = "../webrender_build" } [dependencies] gleam = "0.10.0" diff --git a/swgl/build.rs b/swgl/build.rs index 82050e6e6c..4174a1769b 100644 --- a/swgl/build.rs +++ b/swgl/build.rs @@ -4,12 +4,14 @@ extern crate cc; extern crate glsl_to_cxx; +extern crate webrender_build; use std::collections::HashSet; use std::fmt::Write; +use webrender_build::shader::{ShaderFeatureFlags, get_shader_features}; -fn write_load_shader(shader_keys: &[&str]) { - let shaders: Vec<_> = shader_keys.iter().map(|s| s.replace(':', "_")).collect(); +fn write_load_shader(shader_keys: &[String]) { + let shaders: Vec<_> = shader_keys.iter().map(|s| s.replace(',', "_")).collect(); let mut load_shader = String::new(); for s in &shaders { let _ = write!(load_shader, "#include \"{}.h\"\n", s); @@ -48,7 +50,7 @@ fn translate_shader(shader_key: &str, shader_dir: &str) { imported.push_str("#define SWGL 1\n"); imported.push_str("#define WR_MAX_VERTEX_TEXTURE_WIDTH 1024U\n"); - let mut features = shader_key.split(':'); + let mut features = shader_key.split(','); let basename = features.next().unwrap(); for feature in features { let _ = write!(imported, "#define WR_FEATURE_{}\n", feature); @@ -56,7 +58,7 @@ fn translate_shader(shader_key: &str, shader_dir: &str) { process_imports(shader_dir, basename, &mut HashSet::new(), &mut imported); - let shader = shader_key.replace(':', "_"); + let shader = shader_key.replace(',', "_"); let out_dir = std::env::var("OUT_DIR").unwrap(); let imp_name = format!("{}/{}.c", out_dir, shader); @@ -94,68 +96,27 @@ fn translate_shader(shader_key: &str, shader_dir: &str) { std::fs::write(format!("{}/{}.h", out_dir, shader), result).unwrap(); } -const WR_SHADERS: &'static [&'static str] = &[ - "brush_blend:ALPHA_PASS", - "brush_blend", - "brush_image:ALPHA_PASS", - "brush_image", - "brush_image:REPETITION:ANTIALIASING:ALPHA_PASS", - "brush_image:REPETITION:ANTIALIASING", - "brush_linear_gradient:ALPHA_PASS", - "brush_linear_gradient:DITHERING:ALPHA_PASS", - "brush_linear_gradient:DITHERING", - "brush_linear_gradient", - "brush_mix_blend:ALPHA_PASS", - "brush_mix_blend", - "brush_opacity:ALPHA_PASS", - "brush_radial_gradient:ALPHA_PASS", - "brush_radial_gradient:DITHERING:ALPHA_PASS", - "brush_radial_gradient:DITHERING", - "brush_radial_gradient", - "brush_solid:ALPHA_PASS", - "brush_solid", - "brush_yuv_image", - "brush_yuv_image:TEXTURE_2D:YUV:NV12", - "brush_yuv_image:YUV", - "brush_yuv_image:YUV:ALPHA_PASS", - "brush_yuv_image:YUV:INTERLEAVED", - "brush_yuv_image:YUV:NV12:ALPHA_PASS", - "brush_yuv_image:YUV:NV12", - "brush_yuv_image:YUV:PLANAR", - "composite", - "composite:YUV", - "cs_blur:ALPHA_TARGET", - "cs_blur:COLOR_TARGET", - "cs_border_segment", - "cs_border_solid", - "cs_clip_box_shadow", - "cs_clip_image", - "cs_clip_rectangle:FAST_PATH", - "cs_clip_rectangle", - "cs_gradient", - "cs_line_decoration", - "cs_scale", - "cs_svg_filter", - "debug_color", - "debug_font", - "ps_split_composite", - "ps_text_run:DUAL_SOURCE_BLENDING:ALPHA_PASS", - "ps_text_run:GLYPH_TRANSFORM:ALPHA_PASS", - "ps_text_run:DUAL_SOURCE_BLENDING:GLYPH_TRANSFORM:ALPHA_PASS", - "ps_text_run:ALPHA_PASS", -]; - fn main() { let shader_dir = match std::env::var("MOZ_SRC") { Ok(dir) => dir + "/gfx/wr/webrender/res", Err(_) => std::env::var("CARGO_MANIFEST_DIR").unwrap() + "/../webrender/res", }; - for shader in WR_SHADERS { + let shader_flags = + ShaderFeatureFlags::GL | + ShaderFeatureFlags::DUAL_SOURCE_BLENDING; + let mut shaders: Vec = Vec::new(); + for (name, features) in get_shader_features(shader_flags) { + shaders.extend(features.iter().map(|f| { + if f.is_empty() { name.to_owned() } else { format!("{},{}", name, f) } + })); + } + + for shader in &shaders { translate_shader(shader, &shader_dir); } - write_load_shader(WR_SHADERS); + write_load_shader(&shaders); println!("cargo:rerun-if-changed=src/gl_defs.h"); println!("cargo:rerun-if-changed=src/glsl.h"); diff --git a/swgl/src/glsl.h b/swgl/src/glsl.h index cc94aa3d2d..52528adb0b 100644 --- a/swgl/src/glsl.h +++ b/swgl/src/glsl.h @@ -427,6 +427,10 @@ vec2_scalar make_vec2(float n) { return vec2_scalar{n, n}; } vec2_scalar make_vec2(float x, float y) { return vec2_scalar{x, y}; } +vec2_scalar make_vec2(int32_t x, int32_t y) { + return vec2_scalar{float(x), float(y)}; +} + template vec2 make_vec2(const N& n) { return vec2(n); @@ -1763,6 +1767,9 @@ typedef isampler2D_impl* isampler2D; struct isampler2DRGBA32I_impl : isampler2D_impl {}; typedef isampler2DRGBA32I_impl* isampler2DRGBA32I; +struct sampler2DRect_impl : samplerCommon, samplerFilter {}; +typedef sampler2DRect_impl* sampler2DRect; + struct mat4_scalar; struct mat2_scalar { @@ -2270,6 +2277,13 @@ vec4_scalar texelFetch(sampler2DR8 sampler, ivec2_scalar P, int lod) { 0.0f, 0.0f}; } +vec4 texelFetch(sampler2DRect sampler, ivec2 P) { + P = clamp2D(P, sampler); + assert(sampler->format == TextureFormat::RGBA8); + I32 offset = P.x + P.y * sampler->stride; + return fetchOffsetsRGBA8(sampler, offset); +} + vec4 texelFetch(sampler2DArray sampler, ivec3 P, int lod) { P = clamp2DArray(P, sampler); if (sampler->format == TextureFormat::RGBA32F) { @@ -2643,6 +2657,17 @@ vec4 texture(sampler2D sampler, vec2 P) { } } +vec4 texture(sampler2DRect sampler, vec2 P) { + assert(sampler->format == TextureFormat::RGBA8); + if (sampler->filter == TextureFilter::LINEAR) { + return textureLinearRGBA8(sampler, + P * vec2_scalar{1.0f / sampler->width, 1.0f / sampler->height}); + } else { + ivec2 coord(roundto(P.x, 1.0f), roundto(P.y, 1.0f)); + return texelFetch(sampler, coord); + } +} + vec4 texture(sampler2DArray sampler, vec3 P, Float layer) { assert(0); return vec4(); @@ -2682,6 +2707,10 @@ ivec2_scalar textureSize(sampler2D sampler, int) { return ivec2_scalar{int32_t(sampler->width), int32_t(sampler->height)}; } +ivec2_scalar textureSize(sampler2DRect sampler) { + return ivec2_scalar{int32_t(sampler->width), int32_t(sampler->height)}; +} + ivec4 ivec2::sel(XYZW c1, XYZW c2, XYZW c3, XYZW c4) { return ivec4(select(c1), select(c2), select(c3), select(c4)); } @@ -2764,16 +2793,35 @@ float dot(vec2_scalar a, vec2_scalar b) { return a.x * b.x + a.y * b.y; } Float dot(vec2 a, vec2 b) { return a.x * b.x + a.y * b.y; } #define sin __glsl_sin -#define cos __glsl_cos float sin(float x) { return sinf(x); } Float sin(Float v) { return {sinf(v.x), sinf(v.y), sinf(v.z), sinf(v.w)}; } +#define cos __glsl_cos + float cos(float x) { return cosf(x); } Float cos(Float v) { return {cosf(v.x), cosf(v.y), cosf(v.z), cosf(v.w)}; } +#define tan __glsl_tan + +float tan(float x) { return tanf(x); } + +Float tan(Float v) { return {tanf(v.x), tanf(v.y), tanf(v.z), tanf(v.w)}; } + +#define atan __glsl_atan + +float atan(float x) { return atanf(x); } + +Float atan(Float v) { return {atanf(v.x), atanf(v.y), atanf(v.z), atanf(v.w)}; } + +float atan(float a, float b) { return atan2f(a, b); } + +Float atan(Float a, Float b) { + return {atan2f(a.x, b.x), atan2f(a.y, b.y), atan2f(a.z, b.z), atan2f(a.w, b.w)}; +} + bvec4 notEqual(ivec4 a, ivec4 b) { return bvec4(a.x != b.x, a.y != b.y, a.z != b.z, a.w != b.w); } diff --git a/tileview/src/main.rs b/tileview/src/main.rs index 20758185b4..7011ca35c4 100644 --- a/tileview/src/main.rs +++ b/tileview/src/main.rs @@ -12,6 +12,20 @@ /// 2nd: /// cargo run --release -- /foo/bar/wr-capture/tilecache /tmp/tilecache /// 4. open /tmp/tilecache/index.html +/// +/// Note: accurate interning info requires that the circular buffer doesn't wrap around. +/// So for best results, use this workflow: +/// a. start up blank browser; in about:config enable logging; close browser +/// b. start new browser, quickly load the repro +/// c. capture. +/// +/// If that's tricky, you can also just throw more memory at it: in render_backend.rs, +/// increase the buffer size here: 'TileCacheLogger::new(500usize)' +/// +/// Note: some features don't work when opening index.html directly due to cross-scripting +/// protections. Instead use a HTTP server: +/// python -m SimpleHTTPServer 8000 + use webrender::{TileNode, TileNodeKind, InvalidationReason, TileOffset}; use webrender::{TileSerializer, TileCacheInstanceSerializer, TileCacheLoggerUpdateLists}; @@ -353,6 +367,8 @@ fn slices_to_svg(slices: &[Slice], prev_slices: Option>, let prim_class = format!("tile_slice{}", tile_cache.slice); + svg.push_str(&format!("\n", tile_cache.slice)); + //println!("slice {}", tile_cache.slice); svg.push_str(&format!("\n\n", tile_cache.slice)); @@ -382,6 +398,8 @@ fn slices_to_svg(slices: &[Slice], prev_slices: Option>, &mut invalidation_report, svg_width, svg_height, svg_settings)); } + + svg.push_str("\n"); } ( @@ -398,7 +416,7 @@ fn slices_to_svg(slices: &[Slice], prev_slices: Option>, ) } -fn write_html(output_dir: &Path, svg_files: &[String], intern_files: &[String]) { +fn write_html(output_dir: &Path, max_slice_index: usize, svg_files: &[String], intern_files: &[String]) { let html_head = "\n\ \n\ \n\ @@ -433,6 +451,26 @@ fn write_html(output_dir: &Path, svg_files: &[String], intern_files: &[String]) \n" .to_string(); + let mut html_slices_form = + "\n
\n\ + Slice\n".to_string(); + + for ix in 0..max_slice_index + 1 { + html_slices_form += + &format!( + "\n\ + \n", + ix, + max_slice_index + 1, + ix, + ix ); + } + + html_slices_form += "\n"; + let html_body = format!( "{}\n\
\n\ @@ -451,13 +489,15 @@ fn write_html(output_dir: &Path, svg_files: &[String], intern_files: &[String])
Spacebar to Play
\n\
Use Left/Right to Step
\n\ + {}
", html_body, svg_files[0], svg_files[0], intern_files[0], svg_files[0], - svg_files.len() ); + svg_files.len(), + html_slices_form ); let html = format!("{}{}{}{}", html_head, html_body, script, html_end); @@ -649,7 +689,7 @@ fn main() { prev_slices = Some(slices); } - write_html(output_dir, &svg_files, &intern_files); + write_html(output_dir, max_slice_index, &svg_files, &intern_files); write_css(output_dir, max_slice_index, &svg_settings); std::fs::write(output_dir.join("tilecache.js"), RES_JAVASCRIPT).unwrap(); diff --git a/tileview/src/tilecache.js b/tileview/src/tilecache.js index 36a9fd0aee..5b4f9627d0 100644 --- a/tileview/src/tilecache.js +++ b/tileview/src/tilecache.js @@ -42,6 +42,33 @@ function toggle_quadtree() { } } +function update_slice_visibility(max_slice) { + let content = frontbuffer.contentDocument; + update_slice_visibility_for_content(content, max_slice); +} + +function update_slice_visibility_for_content(content, max_slice) { + + if( !content ) // might happen due to cross-scripting -- use SimpleHTTPServer + return; + + for (let slice = 0; slice != max_slice; ++slice) { + var cbox_name = "slice_toggle" + slice; + let cbox = document.getElementById(cbox_name); + if( !cbox ) + continue; + let checked = cbox.checked; + var id = "tile_slice" + slice + "_everything"; + var group = content.getElementById(id); + if( !group ) + continue; + if( checked ) + group.style.display = "block"; + else + group.style.display = "none"; + } +} + // try to block repeated keypressed from causing flickering // when they land between go_to_svg returning and onload // firing. @@ -66,6 +93,9 @@ function go_to_svg(index) { document.getElementById("text_frame_counter").innerHTML = svg_files[svg_index]; + let content = backbuffer.contentDocument; + update_slice_visibility_for_content(content, 20); + backbuffer.style.display = ''; frontbuffer.style.display = 'none'; diff --git a/webrender/src/device/gl.rs b/webrender/src/device/gl.rs index 68f1bb644f..4bd97420c4 100644 --- a/webrender/src/device/gl.rs +++ b/webrender/src/device/gl.rs @@ -32,8 +32,7 @@ use std::{ thread, time::Duration, }; -use webrender_build::shader::ProgramSourceDigest; -use webrender_build::shader::{ShaderSourceParser, shader_source_from_file}; +use webrender_build::shader::{ProgramSourceDigest, ShaderSourceParser, shader_source_from_file}; /// Sequence number for frames, as tracked by the device layer. #[derive(Debug, Copy, Clone, PartialEq, Ord, Eq, PartialOrd)] @@ -988,6 +987,8 @@ pub struct Capabilities { pub supports_pixel_local_storage: bool, /// Whether advanced blend equations are supported. pub supports_advanced_blend_equation: bool, + /// Whether dual-source blending is supported. + pub supports_dual_source_blending: bool, /// Whether KHR_debug is supported for getting debug messages from /// the driver. pub supports_khr_debug: bool, @@ -1509,6 +1510,12 @@ impl Device { supports_extension(&extensions, "GL_KHR_blend_equation_advanced") && !is_adreno; + let supports_dual_source_blending = match gl.get_type() { + gl::GlType::Gl => supports_extension(&extensions,"GL_ARB_blend_func_extended") && + supports_extension(&extensions,"GL_ARB_explicit_attrib_location"), + gl::GlType::Gles => supports_extension(&extensions,"GL_EXT_blend_func_extended"), + }; + // On the android emulator, glShaderSource can crash if the source // strings are not null-terminated. See bug 1591945. let requires_null_terminated_shader_source = is_emulator; @@ -1542,6 +1549,7 @@ impl Device { supports_blit_to_texture_array, supports_pixel_local_storage, supports_advanced_blend_equation, + supports_dual_source_blending, supports_khr_debug, supports_texture_swizzle, supports_nonzero_pbo_offsets, diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index 1f40e5218f..6e482d7fc8 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -2071,13 +2071,8 @@ impl Renderer { let color_cache_formats = device.preferred_color_formats(); let swizzle_settings = device.swizzle_settings(); - let supports_dual_source_blending = match gl_type { - gl::GlType::Gl => device.supports_extension("GL_ARB_blend_func_extended") && - device.supports_extension("GL_ARB_explicit_attrib_location"), - gl::GlType::Gles => device.supports_extension("GL_EXT_blend_func_extended"), - }; let use_dual_source_blending = - supports_dual_source_blending && + device.get_capabilities().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). @@ -5882,6 +5877,8 @@ impl Renderer { self.shaders .borrow_mut() .pls_init + .as_mut() + .unwrap() .bind( &mut self.device, projection, @@ -5910,6 +5907,8 @@ impl Renderer { self.shaders .borrow_mut() .pls_resolve + .as_mut() + .unwrap() .bind( &mut self.device, projection, diff --git a/webrender/src/shade.rs b/webrender/src/shade.rs index 585917b14f..df646d5a33 100644 --- a/webrender/src/shade.rs +++ b/webrender/src/shade.rs @@ -20,6 +20,8 @@ use time::precise_time_ns; use std::cell::RefCell; use std::rc::Rc; +use webrender_build::shader::{ShaderFeatures, ShaderFeatureFlags, get_shader_features}; + impl ImageBufferKind { pub(crate) fn get_feature_string(&self) -> &'static str { match *self { @@ -34,7 +36,8 @@ impl ImageBufferKind { match (*self, gl_type) { (ImageBufferKind::Texture2D, _) => true, (ImageBufferKind::Texture2DArray, _) => true, - (ImageBufferKind::TextureRect, _) => true, + (ImageBufferKind::TextureRect, &GlType::Gles) => false, + (ImageBufferKind::TextureRect, &GlType::Gl) => true, (ImageBufferKind::TextureExternal, &GlType::Gles) => true, (ImageBufferKind::TextureExternal, &GlType::Gl) => false, } @@ -85,6 +88,7 @@ impl LazilyCompiledShader { features: &[&'static str], device: &mut Device, precache_flags: ShaderPrecacheFlags, + shader_list: &ShaderFeatures, ) -> Result { let mut shader = LazilyCompiledShader { program: None, @@ -96,6 +100,16 @@ impl LazilyCompiledShader { features: features.to_vec(), }; + // 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, + ); + if precache_flags.intersects(ShaderPrecacheFlags::ASYNC_COMPILE | ShaderPrecacheFlags::FULL_COMPILE) { let t0 = precise_time_ns(); shader.get_internal(device, precache_flags)?; @@ -287,8 +301,9 @@ impl BrushShader { device: &mut Device, features: &[&'static str], precache_flags: ShaderPrecacheFlags, - advanced_blend: bool, - dual_source: bool, + shader_list: &ShaderFeatures, + use_advanced_blend: bool, + use_dual_source: bool, use_pixel_local_storage: bool, ) -> Result { let opaque = LazilyCompiledShader::new( @@ -297,6 +312,7 @@ impl BrushShader { features, device, precache_flags, + &shader_list, )?; let mut alpha_features = features.to_vec(); @@ -311,11 +327,10 @@ impl BrushShader { &alpha_features, device, precache_flags, + &shader_list, )?; - let advanced_blend = if advanced_blend && - device.get_capabilities().supports_advanced_blend_equation - { + let advanced_blend = if use_advanced_blend { let mut advanced_blend_features = alpha_features.to_vec(); advanced_blend_features.push(ADVANCED_BLEND_FEATURE); @@ -325,6 +340,7 @@ impl BrushShader { &advanced_blend_features, device, precache_flags, + &shader_list, )?; Some(shader) @@ -332,10 +348,7 @@ impl BrushShader { 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 dual_source = if use_dual_source { let mut dual_source_features = alpha_features.to_vec(); dual_source_features.push(DUAL_SOURCE_FEATURE); @@ -345,6 +358,7 @@ impl BrushShader { &dual_source_features, device, precache_flags, + &shader_list, )?; Some(shader) @@ -361,6 +375,7 @@ impl BrushShader { &debug_overdraw_features, device, precache_flags, + &shader_list, )?; Ok(BrushShader { @@ -420,6 +435,7 @@ impl TextShader { device: &mut Device, features: &[&'static str], precache_flags: ShaderPrecacheFlags, + shader_list: &ShaderFeatures, ) -> Result { let mut simple_features = features.to_vec(); simple_features.push("ALPHA_PASS"); @@ -430,6 +446,7 @@ impl TextShader { &simple_features, device, precache_flags, + &shader_list, )?; let mut glyph_transform_features = features.to_vec(); @@ -442,6 +459,7 @@ impl TextShader { &glyph_transform_features, device, precache_flags, + &shader_list, )?; let mut debug_overdraw_features = features.to_vec(); @@ -453,6 +471,7 @@ impl TextShader { &debug_overdraw_features, device, precache_flags, + &shader_list, )?; Ok(TextShader { simple, glyph_transform, debug_overdraw }) @@ -562,13 +581,13 @@ pub struct Shaders { // 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: TextShader, + pub ps_text_run_dual_source: Option, // Helper shaders for pixel local storage render paths. // pls_init: Initialize pixel local storage, based on current framebuffer value. // pls_resolve: Convert pixel local storage, writing out to fragment value. - pub pls_init: LazilyCompiledShader, - pub pls_resolve: LazilyCompiledShader, + pub pls_init: Option, + pub pls_resolve: Option, ps_split_composite: LazilyCompiledShader, @@ -592,12 +611,33 @@ impl Shaders { let use_pixel_local_storage = device .get_capabilities() .supports_pixel_local_storage; + // 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 use_dual_source_blending = + device.get_capabilities().supports_dual_source_blending && + options.allow_dual_source_blending && + !use_pixel_local_storage; + let use_advanced_blend_equation = + device.get_capabilities().supports_advanced_blend_equation && + options.allow_advanced_blend_equation; + + let mut shader_flags = match gl_type { + GlType::Gl => ShaderFeatureFlags::GL, + GlType::Gles => ShaderFeatureFlags::GLES | ShaderFeatureFlags::TEXTURE_EXTERNAL, + }; + shader_flags.set(ShaderFeatureFlags::PIXEL_LOCAL_STORAGE, use_pixel_local_storage); + 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 */, use_pixel_local_storage, @@ -608,6 +648,7 @@ impl Shaders { device, &[], options.precache_flags, + &shader_list, false /* advanced blend */, false /* dual source */, use_pixel_local_storage, @@ -618,6 +659,7 @@ impl Shaders { device, &[], options.precache_flags, + &shader_list, false /* advanced blend */, false /* dual source */, use_pixel_local_storage, @@ -632,6 +674,7 @@ impl Shaders { &[] }, options.precache_flags, + &shader_list, false /* advanced blend */, false /* dual source */, use_pixel_local_storage, @@ -646,6 +689,7 @@ impl Shaders { &[] }, options.precache_flags, + &shader_list, false /* advanced blend */, false /* dual source */, use_pixel_local_storage, @@ -660,6 +704,7 @@ impl Shaders { &[] }, options.precache_flags, + &shader_list, false /* advanced blend */, false /* dual source */, use_pixel_local_storage, @@ -670,6 +715,7 @@ impl Shaders { device, &[], options.precache_flags, + &shader_list, false /* advanced blend */, false /* dual source */, use_pixel_local_storage, @@ -681,6 +727,7 @@ impl Shaders { &["ALPHA_TARGET"], device, options.precache_flags, + &shader_list, )?; let cs_blur_rgba8 = LazilyCompiledShader::new( @@ -689,6 +736,7 @@ impl Shaders { &["COLOR_TARGET"], device, options.precache_flags, + &shader_list, )?; let cs_svg_filter = LazilyCompiledShader::new( @@ -697,6 +745,7 @@ impl Shaders { &[], device, options.precache_flags, + &shader_list, )?; let cs_clip_rectangle_slow = LazilyCompiledShader::new( @@ -705,6 +754,7 @@ impl Shaders { &[], device, options.precache_flags, + &shader_list, )?; let cs_clip_rectangle_fast = LazilyCompiledShader::new( @@ -713,6 +763,7 @@ impl Shaders { &[FAST_PATH_FEATURE], device, options.precache_flags, + &shader_list, )?; let cs_clip_box_shadow = LazilyCompiledShader::new( @@ -721,6 +772,7 @@ impl Shaders { &[], device, options.precache_flags, + &shader_list, )?; let cs_clip_image = LazilyCompiledShader::new( @@ -729,29 +781,34 @@ impl Shaders { &[], device, options.precache_flags, + &shader_list, )?; - let pls_precache_flags = if use_pixel_local_storage { - options.precache_flags + let pls_init = if use_pixel_local_storage { + Some(LazilyCompiledShader::new( + ShaderKind::Resolve, + "pls_init", + &[PIXEL_LOCAL_STORAGE_FEATURE], + device, + options.precache_flags, + &shader_list, + )?) } else { - ShaderPrecacheFlags::empty() + None }; - let pls_init = LazilyCompiledShader::new( - ShaderKind::Resolve, - "pls_init", - &[PIXEL_LOCAL_STORAGE_FEATURE], - device, - pls_precache_flags, - )?; - - let pls_resolve = LazilyCompiledShader::new( - ShaderKind::Resolve, - "pls_resolve", - &[PIXEL_LOCAL_STORAGE_FEATURE], - device, - pls_precache_flags, - )?; + let pls_resolve = if use_pixel_local_storage { + Some(LazilyCompiledShader::new( + ShaderKind::Resolve, + "pls_resolve", + &[PIXEL_LOCAL_STORAGE_FEATURE], + device, + options.precache_flags, + &shader_list, + )?) + } else { + None + }; let cs_scale = LazilyCompiledShader::new( ShaderKind::Cache(VertexArrayKind::Scale), @@ -759,6 +816,7 @@ impl Shaders { &[], device, options.precache_flags, + &shader_list, )?; // TODO(gw): The split composite + text shader are special cases - the only @@ -773,26 +831,27 @@ impl Shaders { device, &extra_features, options.precache_flags, + &shader_list, )?; - let dual_source_precache_flags = if options.allow_dual_source_blending { - options.precache_flags + let ps_text_run_dual_source = if use_dual_source_blending { + Some(TextShader::new("ps_text_run", + device, + &[DUAL_SOURCE_FEATURE], + options.precache_flags, + &shader_list, + )?) } else { - ShaderPrecacheFlags::empty() + None }; - let ps_text_run_dual_source = TextShader::new("ps_text_run", - device, - &[DUAL_SOURCE_FEATURE], - dual_source_precache_flags, - )?; - let ps_split_composite = LazilyCompiledShader::new( ShaderKind::Primitive, "ps_split_composite", &extra_features, device, options.precache_flags, + &shader_list, )?; // All image configuration. @@ -819,8 +878,9 @@ impl Shaders { device, &image_features, options.precache_flags, - options.allow_advanced_blend_equation, - options.allow_dual_source_blending, + &shader_list, + use_advanced_blend_equation, + use_dual_source_blending, use_pixel_local_storage, )?); @@ -832,8 +892,9 @@ impl Shaders { device, &image_features, options.precache_flags, - options.allow_advanced_blend_equation, - options.allow_dual_source_blending, + &shader_list, + use_advanced_blend_equation, + use_dual_source_blending, use_pixel_local_storage, )?); @@ -864,6 +925,7 @@ impl Shaders { device, &yuv_features, options.precache_flags, + &shader_list, false /* advanced blend */, false /* dual source */, use_pixel_local_storage, @@ -875,6 +937,7 @@ impl Shaders { &yuv_features, device, options.precache_flags, + &shader_list, )?; let index = Self::get_yuv_shader_index( @@ -893,6 +956,7 @@ impl Shaders { &[], device, options.precache_flags, + &shader_list, )?; let cs_gradient = LazilyCompiledShader::new( @@ -901,6 +965,7 @@ impl Shaders { &[], device, options.precache_flags, + &shader_list, )?; let cs_border_segment = LazilyCompiledShader::new( @@ -909,6 +974,7 @@ impl Shaders { &[], device, options.precache_flags, + &shader_list, )?; let cs_border_solid = LazilyCompiledShader::new( @@ -917,6 +983,7 @@ impl Shaders { &[], device, options.precache_flags, + &shader_list, )?; let composite_rgba = LazilyCompiledShader::new( @@ -925,6 +992,7 @@ impl Shaders { &[], device, options.precache_flags, + &shader_list, )?; Ok(Shaders { @@ -1036,7 +1104,7 @@ impl Shaders { } BatchKind::TextRun(glyph_format) => { let text_shader = match key.blend_mode { - BlendMode::SubpixelDualSource => &mut self.ps_text_run_dual_source, + BlendMode::SubpixelDualSource => self.ps_text_run_dual_source.as_mut().unwrap(), _ => &mut self.ps_text_run, }; text_shader.get(glyph_format, debug_flags) @@ -1060,10 +1128,16 @@ impl Shaders { self.cs_clip_rectangle_fast.deinit(device); self.cs_clip_box_shadow.deinit(device); self.cs_clip_image.deinit(device); - self.pls_init.deinit(device); - self.pls_resolve.deinit(device); + if let Some(shader) = self.pls_init { + shader.deinit(device); + } + if let Some(shader) = self.pls_resolve { + shader.deinit(device); + } self.ps_text_run.deinit(device); - self.ps_text_run_dual_source.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); diff --git a/webrender/src/texture_cache.rs b/webrender/src/texture_cache.rs index 418383d997..f9ee1f6179 100644 --- a/webrender/src/texture_cache.rs +++ b/webrender/src/texture_cache.rs @@ -41,7 +41,7 @@ const TEXTURE_REGION_PIXELS: usize = // The minimum number of bytes that we must be able to reclaim in order // to justify clearing the entire shared cache in order to shrink it. -const RECLAIM_THRESHOLD_BYTES: usize = 5 * 1024 * 1024; +const RECLAIM_THRESHOLD_BYTES: usize = 16 * 512 * 512 * 4; /// Items in the texture cache can either be standalone textures, /// or a sub-rect inside the shared cache. @@ -280,7 +280,7 @@ impl SharedTextures { array_alpha8_linear: TextureArray::new( TextureFormatPair::from(ImageFormat::R8), TextureFilter::Linear, - 1, + 4, ), // Used for experimental hdr yuv texture support, but not used in // production Firefox. @@ -315,11 +315,11 @@ impl SharedTextures { } /// Returns the cumulative number of GPU bytes consumed by empty regions. - fn empty_region_bytes(&self) -> usize { - self.array_alpha8_linear.empty_region_bytes() + - self.array_alpha16_linear.empty_region_bytes() + - self.array_color8_linear.empty_region_bytes() + - self.array_color8_nearest.empty_region_bytes() + fn reclaimable_region_bytes(&self) -> usize { + self.array_alpha8_linear.reclaimable_region_bytes() + + self.array_alpha16_linear.reclaimable_region_bytes() + + self.array_color8_linear.reclaimable_region_bytes() + + self.array_color8_nearest.reclaimable_region_bytes() } /// Clears each texture in the set, with the given set of pending updates. @@ -904,7 +904,7 @@ impl TextureCache { // self.require_frame_build flag which is set if we end up calling // clear_shared. debug_assert!(!self.now.is_valid()); - if self.shared_textures.empty_region_bytes() >= RECLAIM_THRESHOLD_BYTES { + if self.shared_textures.reclaimable_region_bytes() >= RECLAIM_THRESHOLD_BYTES { self.reached_reclaim_threshold.get_or_insert(time); } else { self.reached_reclaim_threshold = None; @@ -912,6 +912,7 @@ impl TextureCache { if let Some(t) = self.reached_reclaim_threshold { let dur = time.duration_since(t).unwrap_or_default(); if dur >= Duration::from_secs(5) { + println!("#n !! reclaim shared memory"); self.clear_shared(); self.reached_reclaim_threshold = None; } @@ -935,6 +936,7 @@ impl TextureCache { let do_periodic_gc = time_since_last_gc >= Duration::from_secs(5) && self.shared_textures.size_in_bytes() >= RECLAIM_THRESHOLD_BYTES * 2; if do_periodic_gc { + println!("#n ######## periodic GC"); let threshold = EvictionThresholdBuilder::new(self.now) .max_frames(1) .max_time_s(10) @@ -957,6 +959,11 @@ impl TextureCache { self.expire_old_entries(EntryKind::Standalone, threshold); self.expire_old_entries(EntryKind::Picture, threshold); + self.shared_textures.array_alpha8_linear.release_empty_textures(&mut self.pending_updates); + self.shared_textures.array_alpha16_linear.release_empty_textures(&mut self.pending_updates); + self.shared_textures.array_color8_linear.release_empty_textures(&mut self.pending_updates); + self.shared_textures.array_color8_nearest.release_empty_textures(&mut self.pending_updates); + self.shared_textures.array_alpha8_linear .update_profile(&mut texture_cache_profile.pages_alpha8_linear); self.shared_textures.array_alpha16_linear @@ -1731,6 +1738,10 @@ impl TextureArrayUnit { region.slab_size == slab_size && !region.free_slots.is_empty() }) } + + fn is_empty(&self) -> bool { + self.empty_regions == self.regions.len() + } } /// A texture array contains a number of textures, each with a number of @@ -1766,7 +1777,7 @@ impl TextureArray { } /// Returns the number of GPU bytes consumed by empty regions. - fn empty_region_bytes(&self) -> usize { + fn reclaimable_region_bytes(&self) -> usize { let bpp = self.formats.internal.bytes_per_pixel() as usize; let empty_regions: usize = self.units.iter().map(|u| u.empty_regions).sum(); empty_regions * TEXTURE_REGION_PIXELS * bpp @@ -1778,6 +1789,18 @@ impl TextureArray { } } + fn release_empty_textures(&mut self, updates: &mut TextureUpdateList) { + self.units.retain(|unit| { + if unit.is_empty() { + updates.push_free(unit.texture_id); + + false + } else { + true + } + }); + } + fn update_profile(&self, counter: &mut ResourceProfileCounter) { let num_regions: usize = self.units.iter().map(|u| u.regions.len()).sum(); counter.set(num_regions, self.size_in_bytes()); diff --git a/webrender/tests/angle_shader_validation.rs b/webrender/tests/angle_shader_validation.rs index 4fbac97209..69548e3766 100644 --- a/webrender/tests/angle_shader_validation.rs +++ b/webrender/tests/angle_shader_validation.rs @@ -4,125 +4,17 @@ extern crate mozangle; extern crate webrender; +extern crate webrender_build; use mozangle::shaders::{BuiltInResources, Output, ShaderSpec, ShaderValidator}; +use webrender_build::shader::{ShaderFeatureFlags, get_shader_features}; // from glslang const FRAGMENT_SHADER: u32 = 0x8B30; const VERTEX_SHADER: u32 = 0x8B31; -struct Shader { - name: &'static str, - features: &'static [&'static str], -} - const SHADER_PREFIX: &str = "#define WR_MAX_VERTEX_TEXTURE_WIDTH 1024U\n"; -const BRUSH_FEATURES: &[&str] = &["", "ALPHA_PASS"]; -const CLIP_FEATURES: &[&str] = &[""]; -const FAST_CLIP_FEATURES: &[&str] = &["FAST_PATH"]; -const CACHE_FEATURES: &[&str] = &[""]; -const GRADIENT_FEATURES: &[&str] = &[ "", "DITHERING", "ALPHA_PASS", "DITHERING,ALPHA_PASS" ]; -const PRIM_FEATURES: &[&str] = &[""]; - -const SHADERS: &[Shader] = &[ - // Clip mask shaders - Shader { - name: "cs_clip_rectangle", - features: CLIP_FEATURES, - }, - Shader { - name: "cs_clip_rectangle", - features: FAST_CLIP_FEATURES, - }, - Shader { - name: "cs_clip_image", - features: CLIP_FEATURES, - }, - Shader { - name: "cs_clip_box_shadow", - features: CLIP_FEATURES, - }, - // Cache shaders - Shader { - name: "cs_blur", - features: &[ "ALPHA_TARGET", "COLOR_TARGET" ], - }, - Shader { - name: "cs_border_segment", - features: CACHE_FEATURES, - }, - Shader { - name: "cs_line_decoration", - features: CACHE_FEATURES, - }, - Shader { - name: "cs_gradient", - features: CACHE_FEATURES, - }, - Shader { - name: "cs_border_solid", - features: CACHE_FEATURES, - }, - Shader { - name: "cs_svg_filter", - features: CACHE_FEATURES, - }, - // Prim shaders - Shader { - name: "ps_split_composite", - features: PRIM_FEATURES, - }, - Shader { - name: "ps_text_run", - features: &[ "", "GLYPH_TRANSFORM" ], - }, - // Brush shaders - Shader { - name: "brush_yuv_image", - features: &[ - "", - "YUV_NV12", - "YUV_PLANAR", - "YUV_INTERLEAVED", - "TEXTURE_2D,YUV_NV12", - "YUV_NV12,ALPHA_PASS", - ], - }, - Shader { - name: "brush_solid", - features: BRUSH_FEATURES, - }, - Shader { - name: "brush_image", - features: BRUSH_FEATURES, - }, - Shader { - name: "brush_blend", - features: BRUSH_FEATURES, - }, - Shader { - name: "brush_mix_blend", - features: BRUSH_FEATURES, - }, - Shader { - name: "brush_conic_gradient", - features: GRADIENT_FEATURES, - }, - Shader { - name: "brush_radial_gradient", - features: GRADIENT_FEATURES, - }, - Shader { - name: "brush_linear_gradient", - features: GRADIENT_FEATURES, - }, - Shader { - name: "brush_opacity", - features: BRUSH_FEATURES, - }, -]; - const VERSION_STRING: &str = "#version 300 es\n"; #[test] @@ -136,8 +28,8 @@ fn validate_shaders() { let fs_validator = ShaderValidator::new(FRAGMENT_SHADER, ShaderSpec::Gles3, Output::Essl, &resources).unwrap(); - for shader in SHADERS { - for config in shader.features { + for (shader, configs) in get_shader_features(ShaderFeatureFlags::GLES) { + for config in configs { let mut features = String::new(); features.push_str(SHADER_PREFIX); @@ -146,10 +38,10 @@ fn validate_shaders() { } let (vs, fs) = - webrender::build_shader_strings(VERSION_STRING, &features, shader.name, None); + webrender::build_shader_strings(VERSION_STRING, &features, shader, None); - validate(&vs_validator, shader.name, vs); - validate(&fs_validator, shader.name, fs); + validate(&vs_validator, shader, vs); + validate(&fs_validator, shader, fs); } } } diff --git a/webrender_build/Cargo.toml b/webrender_build/Cargo.toml index 411118dc3f..e8637996b1 100644 --- a/webrender_build/Cargo.toml +++ b/webrender_build/Cargo.toml @@ -11,4 +11,5 @@ edition = "2018" serialize_program = ["serde"] [dependencies] +bitflags = "1.2" serde = { optional = true, version = "1.0", features = ["serde_derive"] } diff --git a/webrender_build/src/lib.rs b/webrender_build/src/lib.rs index 0abd144725..1d6f19797e 100644 --- a/webrender_build/src/lib.rs +++ b/webrender_build/src/lib.rs @@ -2,8 +2,12 @@ * 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/. */ +#[macro_use] +extern crate bitflags; + #[cfg(any(feature = "serde"))] #[macro_use] extern crate serde; pub mod shader; +mod shader_features; diff --git a/webrender_build/src/shader.rs b/webrender_build/src/shader.rs index 48367f3658..a564ef2591 100644 --- a/webrender_build/src/shader.rs +++ b/webrender_build/src/shader.rs @@ -14,6 +14,8 @@ use std::path::Path; use std::collections::HashSet; use std::collections::hash_map::DefaultHasher; +pub use crate::shader_features::*; + #[derive(PartialEq, Eq, Hash, Debug, Clone, Default)] #[cfg_attr(feature = "serialize_program", derive(Deserialize, Serialize))] pub struct ProgramSourceDigest(u64); diff --git a/webrender_build/src/shader_features.rs b/webrender_build/src/shader_features.rs new file mode 100644 index 0000000000..997616b0f4 --- /dev/null +++ b/webrender_build/src/shader_features.rs @@ -0,0 +1,146 @@ +/* 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 std::collections::HashMap; + +bitflags! { + #[derive(Default)] + pub struct ShaderFeatureFlags: u32 { + const GL = 1 << 0; + const GLES = 1 << 1; + + const ADVANCED_BLEND_EQUATION = 1 << 8; + const DUAL_SOURCE_BLENDING = 1 << 9; + const PIXEL_LOCAL_STORAGE = 1 << 10; + const DITHERING = 1 << 11; + const TEXTURE_EXTERNAL = 1 << 12; + } +} + +pub type ShaderFeatures = HashMap<&'static str, Vec>; + +macro_rules! features { + ($($str:expr),*) => { vec![$(String::from($str),)*] as Vec }; +} + +fn concat_features(a: &str, b: &str) -> String { + if a.is_empty() { + b.to_string() + } else if b.is_empty() { + a.to_string() + } else { + [a, b].join(",") + } +} + +/// Computes available shaders and their features for the given feature flags. +pub fn get_shader_features(flags: ShaderFeatureFlags) -> ShaderFeatures { + let mut shaders = ShaderFeatures::new(); + + // Clip shaders + shaders.insert("cs_clip_rectangle", features!["", "FAST_PATH"]); + for name in &["cs_clip_image", "cs_clip_box_shadow"] { + shaders.insert(name, features![""]); + } + + // Cache shaders + shaders.insert("cs_blur", features!["ALPHA_TARGET", "COLOR_TARGET"]); + + for name in &["cs_line_decoration", "cs_gradient", "cs_border_segment", "cs_border_solid", "cs_svg_filter"] { + shaders.insert(name, features![""]); + } + + shaders.insert("cs_scale", features![""]); + + // Pixel local storage shaders + let pls_feature = if flags.contains(ShaderFeatureFlags::PIXEL_LOCAL_STORAGE) { + for name in &["pls_init", "pls_resolve"] { + shaders.insert(name, features!["PIXEL_LOCAL_STORAGE"]); + } + + "PIXEL_LOCAL_STORAGE" + } else { + "" + }; + + // Brush shaders + let brush_alpha_features = concat_features("ALPHA_PASS", pls_feature); + for name in &["brush_solid", "brush_blend", "brush_mix_blend", "brush_opacity"] { + shaders.insert(name, features!["", &brush_alpha_features, "DEBUG_OVERDRAW"]); + } + for name in &["brush_conic_gradient", "brush_radial_gradient", "brush_linear_gradient"] { + let mut features: Vec = Vec::new(); + let base = if flags.contains(ShaderFeatureFlags::DITHERING) { + "DITHERING" + } else { + "" + }; + features.push(base.to_string()); + features.push(concat_features(base, &brush_alpha_features)); + features.push(concat_features(base, "DEBUG_OVERDRAW")); + shaders.insert(name, features); + } + + // Image brush shaders + let mut texture_types = vec!["", "TEXTURE_2D"]; + if flags.contains(ShaderFeatureFlags::GL) { + texture_types.push("TEXTURE_RECT"); + } + if flags.contains(ShaderFeatureFlags::TEXTURE_EXTERNAL) { + texture_types.push("TEXTURE_EXTERNAL"); + } + let mut image_features: Vec = Vec::new(); + for texture_type in &texture_types { + let fast = texture_type.to_string(); + image_features.push(fast.clone()); + image_features.push(concat_features(&fast, &brush_alpha_features)); + image_features.push(concat_features(&fast, "DEBUG_OVERDRAW")); + let slow = concat_features(texture_type, "REPETITION,ANTIALIASING"); + image_features.push(slow.clone()); + image_features.push(concat_features(&slow, &brush_alpha_features)); + image_features.push(concat_features(&slow, "DEBUG_OVERDRAW")); + if flags.contains(ShaderFeatureFlags::ADVANCED_BLEND_EQUATION) { + let advanced_blend_features = concat_features(&brush_alpha_features, "ADVANCED_BLEND"); + image_features.push(concat_features(&fast, &advanced_blend_features)); + image_features.push(concat_features(&slow, &advanced_blend_features)); + } + if flags.contains(ShaderFeatureFlags::DUAL_SOURCE_BLENDING) { + let dual_source_features = concat_features(&brush_alpha_features, "DUAL_SOURCE_BLENDING"); + image_features.push(concat_features(&fast, &dual_source_features)); + image_features.push(concat_features(&slow, &dual_source_features)); + } + } + shaders.insert("brush_image", image_features); + + // YUV image brush shaders + let mut composite_features: Vec = vec!["".to_string()]; + let mut yuv_features: Vec = Vec::new(); + for texture_type in &texture_types { + let base = concat_features("YUV", texture_type); + composite_features.push(base.clone()); + yuv_features.push(base.clone()); + yuv_features.push(concat_features(&base, &brush_alpha_features)); + yuv_features.push(concat_features(&base, "DEBUG_OVERDRAW")); + } + shaders.insert("composite", composite_features); + shaders.insert("brush_yuv_image", yuv_features); + + // Prim shaders + let mut text_types = vec![pls_feature]; + if flags.contains(ShaderFeatureFlags::DUAL_SOURCE_BLENDING) { + text_types.push("DUAL_SOURCE_BLENDING"); + } + let mut text_features: Vec = Vec::new(); + for text_type in &text_types { + text_features.push(concat_features(text_type, "ALPHA_PASS")); + text_features.push(concat_features(text_type, "GLYPH_TRANSFORM,ALPHA_PASS")); + text_features.push(concat_features(text_type, "DEBUG_OVERDRAW")); + } + shaders.insert("ps_text_run", text_features); + + shaders.insert("ps_split_composite", features![pls_feature]); + + shaders +} +