From fd7d9482da1282b26c0835e9fca8f06b3f585bea Mon Sep 17 00:00:00 2001 From: Sean Feng Date: Fri, 13 Mar 2020 04:30:53 +0000 Subject: [PATCH 01/23] Bug 1600793 - Make the scrolling input telemetry work for WebRender r=botond,jrmuizel Differential Revision: https://phabricator.services.mozilla.com/D60046 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/b781fe3bde1aed855813cc97f7250885a3512a0c --- webrender/src/lib.rs | 1 + webrender/src/render_backend.rs | 7 ++++--- webrender/src/renderer.rs | 3 ++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/webrender/src/lib.rs b/webrender/src/lib.rs index 0aeb12b3dd..ca3d5bbb3c 100644 --- a/webrender/src/lib.rs +++ b/webrender/src/lib.rs @@ -218,6 +218,7 @@ pub use crate::renderer::{ MAX_VERTEX_TEXTURE_WIDTH, }; pub use crate::hit_test::SharedHitTester; +pub use crate::internal_types::FastHashMap; pub use crate::screen_capture::{AsyncScreenshotHandle, RecordedFrameHandle}; pub use crate::shade::{Shaders, WrShaders}; pub use api as webrender_api; diff --git a/webrender/src/render_backend.rs b/webrender/src/render_backend.rs index c3887efc4a..358b4a1221 100644 --- a/webrender/src/render_backend.rs +++ b/webrender/src/render_backend.rs @@ -1512,6 +1512,8 @@ impl RenderBackend { ) { let requested_frame = render_frame; + let requires_frame_build = self.requires_frame_build(); + let doc = self.documents.get_mut(&document_id).unwrap(); // If we have a sampler, get more frame ops from it and add them // to the transaction. This is a hook to allow the WR user code to // fiddle with things after a potentially long scene build, but just @@ -1519,12 +1521,11 @@ impl RenderBackend { // async transforms. if requested_frame || has_built_scene { if let Some(ref sampler) = self.sampler { - frame_ops.append(&mut sampler.sample(document_id)); + frame_ops.append(&mut sampler.sample(document_id, + &doc.scene.pipeline_epochs)); } } - let requires_frame_build = self.requires_frame_build(); - let doc = self.documents.get_mut(&document_id).unwrap(); doc.has_built_scene |= has_built_scene; // TODO: this scroll variable doesn't necessarily mean we scrolled. It is only used diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index c1860cc142..f29a895b32 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -6511,7 +6511,8 @@ pub trait AsyncPropertySampler { /// This is called for each transaction with the generate_frame flag set /// (i.e. that will trigger a render). The list of frame messages returned /// are processed as though they were part of the original transaction. - fn sample(&self, document_id: DocumentId) -> Vec; + fn sample(&self, document_id: DocumentId, + doc: &FastHashMap) -> Vec; /// This is called exactly once, when the render backend thread is about to /// terminate. fn deregister(&self); From a1d59387904db7efd5b418163d7c3b47ccd455eb Mon Sep 17 00:00:00 2001 From: dev Date: Fri, 13 Mar 2020 04:31:02 +0000 Subject: [PATCH 02/23] Bug 1621758 - Fix occlusion rect of clipped compositor surfaces. r=mstange Differential Revision: https://phabricator.services.mozilla.com/D66506 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/4bd0143d7cbe6e8271e36f449de2673e70d4b9b4 --- webrender/src/composite.rs | 1 + webrender/src/picture.rs | 35 ++++++++++++++++++--------- wrench/reftests/image/occlusion.png | Bin 0 -> 49427 bytes wrench/reftests/image/occlusion.yaml | 15 ++++++++++++ wrench/reftests/image/reftest.list | 1 + wrench/reftests/mask/reftest.list | 2 +- 6 files changed, 42 insertions(+), 12 deletions(-) create mode 100644 wrench/reftests/image/occlusion.png create mode 100644 wrench/reftests/image/occlusion.yaml diff --git a/webrender/src/composite.rs b/webrender/src/composite.rs index 47880abac6..0312b9f68c 100644 --- a/webrender/src/composite.rs +++ b/webrender/src/composite.rs @@ -94,6 +94,7 @@ pub struct ExternalSurfaceDescriptor { pub local_rect: PictureRect, pub world_rect: WorldRect, pub device_rect: DeviceRect, + pub local_clip_rect: PictureRect, pub clip_rect: DeviceRect, pub image_dependencies: [ImageDependency; 3], pub image_rendering: ImageRendering, diff --git a/webrender/src/picture.rs b/webrender/src/picture.rs index f4faf055fe..747f011e3f 100644 --- a/webrender/src/picture.rs +++ b/webrender/src/picture.rs @@ -3175,6 +3175,7 @@ impl TileCacheInstance { self.external_surfaces.push(ExternalSurfaceDescriptor { local_rect: prim_info.prim_clip_rect, world_rect, + local_clip_rect: prim_info.prim_clip_rect, image_dependencies, image_rendering: prim_data.kind.image_rendering, device_rect, @@ -3370,6 +3371,13 @@ impl TileCacheInstance { }; } + let map_pic_to_world = SpaceMapper::new_with_target( + ROOT_SPATIAL_NODE_INDEX, + self.spatial_node_index, + frame_context.global_screen_world_rect, + frame_context.spatial_tree, + ); + // Register the opaque region of this tile cache as an occluder, which // is used later in the frame to occlude other tiles. if self.backdrop.rect.is_well_formed_and_nonempty() { @@ -3380,13 +3388,6 @@ impl TileCacheInstance { }); if let Some(backdrop_rect) = backdrop_rect { - let map_pic_to_world = SpaceMapper::new_with_target( - ROOT_SPATIAL_NODE_INDEX, - self.spatial_node_index, - frame_context.global_screen_world_rect, - frame_context.spatial_tree, - ); - let world_backdrop_rect = map_pic_to_world .map(&backdrop_rect) .expect("bug: unable to map backdrop to world space"); @@ -3405,10 +3406,22 @@ impl TileCacheInstance { // able to occlude every background tile (avoiding allocation, rasterizion // and compositing). for external_surface in &self.external_surfaces { - frame_state.composite_state.register_occluder( - external_surface.z_id, - external_surface.world_rect, - ); + let local_surface_rect = external_surface.local_rect + .intersection(&external_surface.local_clip_rect) + .and_then(|r| { + r.intersection(&self.local_clip_rect) + }); + + if let Some(local_surface_rect) = local_surface_rect { + let world_surface_rect = map_pic_to_world + .map(&local_surface_rect) + .expect("bug: unable to map external surface to world space"); + + frame_state.composite_state.register_occluder( + external_surface.z_id, + world_surface_rect, + ); + } } // A simple GC of the native external surface cache, to remove and free any diff --git a/wrench/reftests/image/occlusion.png b/wrench/reftests/image/occlusion.png new file mode 100644 index 0000000000000000000000000000000000000000..c62a58cdec849b2bba4299468bd6d3d66cec7863 GIT binary patch literal 49427 zcmeFY^-mq&8#UVE?pC0;memV$kI|`Dqp^Q<@r1);b1;zoVeeaKTlAuBGPJb zpTiH%EaJ--k}uL?!fIaG=Uuwh_G9k34>8K>t~H3=B*S156(>5|W-aQZNA?UTXs$ z6gLZtlQ}D#!{@?rlmCC=|GRO>0?J^o?L$UAeV`yAKR&`kr4ksV>xGcu7G#g zFHd8EkVsCD>S(9o3rxR)5YizLEbeYdbpojk3~hg@e#9*))zk3B-6Qw%V+}zK@VJIrj!+*St7C_&09mZHTQmz4iE42roVpQ8^u7e7qX&Od~Eo z9tu6+K7kBB*n}Pk40orB2(n$K$2DIOn19l~uy(ZE5@%O^RNKc^=U^1co8>?{<|S}B z&I$(dF@d+{Yovt&ggk{DdMpaGeEReZaRD{=k&7k67k`q=GIZ)3B?{-h+yDNigiEr8G<;cHMl&YH>o2W=_uVfS4>4xNUBhN0++Nmsjl|^5ZA>X% z#R5(8y;fAQU^P6towd{54EDN4F4geI@KdYWACG5^`lrY3WUqMc$bCPjt6$!`NnFk`-Q%p-7|RWav>WOZU{%-%O$^*E-9ui28J z4FvSrQ0Gec&5xGqy1a4L`~q{z5R}=z5uxA!OYgni0^f6%B*+s3qMJuc&^tst$O)1! zpp9LpsRi9|*HedO3%V6d2y{a+=%horVg)U?j;eJV3Vr8=>2|Rl_irIpE}>TOCfDmb zr-J_RUX1)%ZV3FYRo)>)WT4BN9`gRe#k>WG`SJTLU-)c5ZZ(OUxOKQ-QxZ}Uc_6q0 zNsUC+aqZIM9-8cY^E>wFl+M`+j@Z+VYR=p8F2p$ns1d^NBXF(F?0Dn?30cH5!wL%P z0nt^mrv0O>qt1U9ph=b>5!Iz*IH6v4NUxyR#lC?35P7?Wy>p&j`!?BpeO9R=CBg-Vw*Q z`%ios*EDL?yAe;MBG45!2Os|MSqGg26y|G%OvF^@U>?>5JvXu>jeIz!l_z2qD!ZZk z9lS-MV(R$&vK}^K+QxrSv*%xy-@a^5-~DY*A8}{JiM(-^rr)jeuI83HKLt&jPSe|K z&W^R2y}4t~<;F`>OLB2r%a&jk0UF9M#T4}A)dMv#R+H-;Z1S)~u=&TkAnaSFO!+0% zg`bDfs*kI&r|K4i1had!u+2#!@hqP=!VBIGd%Pcw#{7hQGWCC`E+&df8yN}P7nfB7A|#8YbF

bZPM!h}P&``+e|4P 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 z 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%0A~ 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 literal 0 HcmV?d00001 diff --git a/wrench/reftests/image/occlusion.yaml b/wrench/reftests/image/occlusion.yaml new file mode 100644 index 0000000000..4e89a7765b --- /dev/null +++ b/wrench/reftests/image/occlusion.yaml @@ -0,0 +1,15 @@ +# Ensure that the clip rect of a primitive that is promoted to a compositor +# surface is correctly applied when registering it as an occlusion plane. + +root: + items: + - type: rect + color: red + bounds: [50, 50, 200, 200] + + - type: yuv-image + format: interleaved + src: spacex-yuv.png + bounds: [50, 50, 200, 200] + prefer-compositor-surface: true + clip-rect: [75, 75, 150, 150] diff --git a/wrench/reftests/image/reftest.list b/wrench/reftests/image/reftest.list index 733eff8cd2..c41eb8b3c9 100644 --- a/wrench/reftests/image/reftest.list +++ b/wrench/reftests/image/reftest.list @@ -9,3 +9,4 @@ platform(linux,mac) == yuv.yaml yuv.png skip_on(android,device) == tiled-clip-chain.yaml tiled-clip-chain-ref.yaml skip_on(android,device) == tiled-complex-clip.yaml tiled-complex-clip-ref.yaml platform(linux,mac) == texture-rect.yaml texture-rect-ref.yaml +platform(linux) == occlusion.yaml occlusion.png diff --git a/wrench/reftests/mask/reftest.list b/wrench/reftests/mask/reftest.list index 77e336f339..7a18b4a010 100644 --- a/wrench/reftests/mask/reftest.list +++ b/wrench/reftests/mask/reftest.list @@ -10,7 +10,7 @@ platform(linux,mac) == rounded-corners.yaml rounded-corners.png platform(linux,mac) fuzzy(1,17500) color_targets(3) alpha_targets(1) == mask-atomicity.yaml mask-atomicity-ref.yaml platform(linux,mac) fuzzy(1,17500) == mask-atomicity-tiling.yaml mask-atomicity-ref.yaml platform(linux,mac) == mask-perspective.yaml mask-perspective.png -== fuzzy(1,7) mask-perspective-tiling.yaml mask-perspective.yaml +== fuzzy(1,11) mask-perspective-tiling.yaml mask-perspective.yaml platform(linux,mac) == checkerboard.yaml checkerboard.png skip_on(android,device) == checkerboard.yaml checkerboard-tiling.yaml # Fails on a Pixel2 == missing-mask.yaml missing-mask-ref.yaml From 0fe639d18aec4bd6b631035997309161b81eecb2 Mon Sep 17 00:00:00 2001 From: Jeff Muizelaar Date: Fri, 13 Mar 2020 21:53:52 +0000 Subject: [PATCH 03/23] Bug 1622327. Add support for adding profiling event markers to WebRender. r=kvark I plan on using this to add texture cache reallocation events. Differential Revision: https://phabricator.services.mozilla.com/D66792 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/2f04abdb752c6d2cc25882d2ee4783aed2a8e8eb --- webrender/src/profiler.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/webrender/src/profiler.rs b/webrender/src/profiler.rs index e28d70ccde..da3f6902f9 100644 --- a/webrender/src/profiler.rs +++ b/webrender/src/profiler.rs @@ -82,6 +82,10 @@ pub trait ProfilerHooks : Send + Sync { /// be a C string (null terminated). fn end_marker(&self, label: &CStr); + /// Called to mark an event happening. The label must + /// be a C string (null terminated). + fn event_marker(&self, label: &CStr); + /// Called with a duration to indicate a text marker that just ended. Text /// markers allow different types of entries to be recorded on the same row /// in the timeline, by adding labels to the entry. @@ -123,6 +127,15 @@ pub fn add_text_marker(label: &CStr, text: &str, duration: Duration) { } } +/// Records a marker of the given duration that just ended. +pub fn add_event_marker(label: &CStr) { + unsafe { + if let Some(ref hooks) = PROFILER_HOOKS { + hooks.event_marker(label); + } + } +} + /// Returns true if the current thread is being profiled. pub fn thread_is_being_profiled() -> bool { unsafe { From f17d6131d40980e17c8459abf59347373fb6db67 Mon Sep 17 00:00:00 2001 From: Coroiu Cristina Date: Fri, 13 Mar 2020 21:54:01 +0000 Subject: [PATCH 04/23] Backed out changeset 2f04abdb752c (bug 1622327) for build bustage on a CLOSED TREE [ghsync] From https://hg.mozilla.org/mozilla-central/rev/23925b6dcab718c6ad0dcec41e1f48c2a30cabcd --- webrender/src/profiler.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/webrender/src/profiler.rs b/webrender/src/profiler.rs index da3f6902f9..e28d70ccde 100644 --- a/webrender/src/profiler.rs +++ b/webrender/src/profiler.rs @@ -82,10 +82,6 @@ pub trait ProfilerHooks : Send + Sync { /// be a C string (null terminated). fn end_marker(&self, label: &CStr); - /// Called to mark an event happening. The label must - /// be a C string (null terminated). - fn event_marker(&self, label: &CStr); - /// Called with a duration to indicate a text marker that just ended. Text /// markers allow different types of entries to be recorded on the same row /// in the timeline, by adding labels to the entry. @@ -127,15 +123,6 @@ pub fn add_text_marker(label: &CStr, text: &str, duration: Duration) { } } -/// Records a marker of the given duration that just ended. -pub fn add_event_marker(label: &CStr) { - unsafe { - if let Some(ref hooks) = PROFILER_HOOKS { - hooks.event_marker(label); - } - } -} - /// Returns true if the current thread is being profiled. pub fn thread_is_being_profiled() -> bool { unsafe { From 9f2341a50760638f83f5ac351e78597c8a289e17 Mon Sep 17 00:00:00 2001 From: Lee Salzman Date: Sat, 14 Mar 2020 10:35:50 +0000 Subject: [PATCH 05/23] Bug 1612941 - add deinit hook to WR Compositor trait. r=gw Differential Revision: https://phabricator.services.mozilla.com/D65591 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/bdf468af2e58f9dc651f8d062a3da90be8a412a5 --- webrender/src/composite.rs | 3 +++ webrender/src/renderer.rs | 1 + 2 files changed, 4 insertions(+) diff --git a/webrender/src/composite.rs b/webrender/src/composite.rs index 0312b9f68c..20b6244926 100644 --- a/webrender/src/composite.rs +++ b/webrender/src/composite.rs @@ -757,6 +757,9 @@ pub trait Compositor { /// Enable/disable native compositor usage fn enable_native_compositor(&mut self, enable: bool); + /// Safely deinitialize any remaining resources owned by the compositor. + fn deinit(&mut self); + /// Get the capabilities struct for this compositor. This is used to /// specify what features a compositor supports, depending on the /// underlying platform diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index f29a895b32..5f1eb9f18d 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -6312,6 +6312,7 @@ impl Renderer { if self.debug_overlay_state.current_size.is_some() { compositor.destroy_surface(NativeSurfaceId::DEBUG_OVERLAY); } + compositor.deinit(); } self.gpu_cache_texture.deinit(&mut self.device); if let Some(dither_matrix_texture) = self.dither_matrix_texture { From d91904103588166ab59d412227f008b82e8533c8 Mon Sep 17 00:00:00 2001 From: Lee Salzman Date: Sat, 14 Mar 2020 10:36:00 +0000 Subject: [PATCH 06/23] Bug 1612941 - add SWGL glue to wrench. r=jrmuizel Differential Revision: https://phabricator.services.mozilla.com/D65593 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/3c5eddbc74b6c68fd45edac8dbc8fcf0433b08c7 --- wrench/Cargo.toml | 2 + wrench/src/args.yaml | 3 + wrench/src/main.rs | 202 ++++++++++++++++++++++++++++++++++--------- 3 files changed, 165 insertions(+), 42 deletions(-) diff --git a/wrench/Cargo.toml b/wrench/Cargo.toml index 247057b179..bbc24264d6 100644 --- a/wrench/Cargo.toml +++ b/wrench/Cargo.toml @@ -29,6 +29,7 @@ webrender_api = {path = "../webrender_api", features=["serialize","deserialize"] winit = "0.19" serde = {version = "1.0", features = ["derive"] } semver = "0.9.0" +swgl = { path = "../swgl", optional = true } [dependencies.image] version = "0.23" @@ -42,6 +43,7 @@ core-foundation = "0.6.4" [features] default = [ "env_logger" ] headless = [ "osmesa-sys", "osmesa-src" ] +software = [ "swgl" ] [target.'cfg(target_os = "windows")'.dependencies] dwrote = "0.9" diff --git a/wrench/src/args.yaml b/wrench/src/args.yaml index 1567e994cc..7080b1bbdb 100644 --- a/wrench/src/args.yaml +++ b/wrench/src/args.yaml @@ -46,6 +46,9 @@ args: - angle: long: angle help: Enable ANGLE rendering (on Windows only) + - software: + long: software + help: Enable software rendering - dp_ratio: short: p long: device-pixel-ratio diff --git a/wrench/src/main.rs b/wrench/src/main.rs index cb2fafef71..7a7fe2bc99 100644 --- a/wrench/src/main.rs +++ b/wrench/src/main.rs @@ -30,6 +30,8 @@ mod cgfont_to_data; use crate::binary_frame_reader::BinaryFrameReader; use gleam::gl; +#[cfg(feature = "software")] +use gleam::gl::Gl; use crate::perf::PerfHarness; use crate::png::save_flipped; use crate::rawtest::RawtestHarness; @@ -44,6 +46,8 @@ use std::path::{Path, PathBuf}; use std::process; use std::ptr; use std::rc::Rc; +#[cfg(feature = "software")] +use std::slice; use std::sync::mpsc::{channel, Sender, Receiver}; use webrender::DebugFlags; use webrender::api::*; @@ -129,22 +133,57 @@ impl HeadlessContext { } } +#[cfg(not(feature = "software"))] +mod swgl { + pub struct Context; +} + pub enum WindowWrapper { - WindowedContext(glutin::WindowedContext, Rc), - Angle(winit::Window, angle::Context, Rc), - Headless(HeadlessContext, Rc), + WindowedContext(glutin::WindowedContext, Rc, Option), + Angle(winit::Window, angle::Context, Rc, Option), + Headless(HeadlessContext, Rc, Option), } pub struct HeadlessEventIterater; impl WindowWrapper { + #[cfg(feature = "software")] + fn upload_software_to_native(&self) { + match *self { + WindowWrapper::Headless(..) => return, + _ => {} + } + let swgl = match self.software_gl() { + Some(swgl) => swgl, + None => return, + }; + swgl.finish(); + let gl = self.native_gl(); + let tex = gl.gen_textures(1)[0]; + gl.bind_texture(gl::TEXTURE_2D, tex); + let (data_ptr, w, h) = swgl.get_color_buffer(0, true); + let buffer = unsafe { slice::from_raw_parts(data_ptr as *const u8, w as usize * h as usize * 4) }; + gl.tex_image_2d(gl::TEXTURE_2D, 0, gl::RGBA8 as gl::GLint, w, h, 0, gl::BGRA, gl::UNSIGNED_BYTE, Some(buffer)); + let fb = gl.gen_framebuffers(1)[0]; + gl.bind_framebuffer(gl::READ_FRAMEBUFFER, fb); + gl.framebuffer_texture_2d(gl::READ_FRAMEBUFFER, gl::COLOR_ATTACHMENT0, gl::TEXTURE_2D, tex, 0); + gl.blit_framebuffer(0, 0, w, h, 0, 0, w, h, gl::COLOR_BUFFER_BIT, gl::NEAREST); + gl.delete_framebuffers(&[fb]); + gl.delete_textures(&[tex]); + gl.finish(); + } + + #[cfg(not(feature = "software"))] + fn upload_software_to_native(&self) { + } + fn swap_buffers(&self) { match *self { - WindowWrapper::WindowedContext(ref windowed_context, _) => { + WindowWrapper::WindowedContext(ref windowed_context, _, _) => { windowed_context.swap_buffers().unwrap() } - WindowWrapper::Angle(_, ref context, _) => context.swap_buffers().unwrap(), - WindowWrapper::Headless(_, _) => {} + WindowWrapper::Angle(_, ref context, _, _) => context.swap_buffers().unwrap(), + WindowWrapper::Headless(_, _, _) => {} } } @@ -157,62 +196,122 @@ impl WindowWrapper { DeviceIntSize::new(size.width as i32, size.height as i32) } match *self { - WindowWrapper::WindowedContext(ref windowed_context, _) => { + WindowWrapper::WindowedContext(ref windowed_context, ..) => { inner_size(windowed_context.window()) } WindowWrapper::Angle(ref window, ..) => inner_size(window), - WindowWrapper::Headless(ref context, _) => DeviceIntSize::new(context.width, context.height), + WindowWrapper::Headless(ref context, ..) => DeviceIntSize::new(context.width, context.height), } } fn hidpi_factor(&self) -> f32 { match *self { - WindowWrapper::WindowedContext(ref windowed_context, _) => { + WindowWrapper::WindowedContext(ref windowed_context, ..) => { windowed_context.window().get_hidpi_factor() as f32 } WindowWrapper::Angle(ref window, ..) => window.get_hidpi_factor() as f32, - WindowWrapper::Headless(_, _) => 1.0, + WindowWrapper::Headless(..) => 1.0, } } fn resize(&mut self, size: DeviceIntSize) { match *self { - WindowWrapper::WindowedContext(ref mut windowed_context, _) => { + WindowWrapper::WindowedContext(ref mut windowed_context, ..) => { windowed_context.window() .set_inner_size(LogicalSize::new(size.width as f64, size.height as f64)) }, WindowWrapper::Angle(ref mut window, ..) => { window.set_inner_size(LogicalSize::new(size.width as f64, size.height as f64)) }, - WindowWrapper::Headless(_, _) => unimplemented!(), // requites Glutin update + WindowWrapper::Headless(..) => unimplemented!(), // requites Glutin update } } fn set_title(&mut self, title: &str) { match *self { - WindowWrapper::WindowedContext(ref windowed_context, _) => { + WindowWrapper::WindowedContext(ref windowed_context, ..) => { windowed_context.window().set_title(title) } WindowWrapper::Angle(ref window, ..) => window.set_title(title), - WindowWrapper::Headless(_, _) => (), + WindowWrapper::Headless(..) => (), } } - pub fn gl(&self) -> &dyn gl::Gl { + #[cfg(feature = "software")] + pub fn software_gl(&self) -> Option<&swgl::Context> { + match *self { + WindowWrapper::WindowedContext(_, _, ref swgl) | + WindowWrapper::Angle(_, _, _, ref swgl) | + WindowWrapper::Headless(_, _, ref swgl) => swgl.as_ref(), + } + } + + pub fn native_gl(&self) -> &dyn gl::Gl { match *self { - WindowWrapper::WindowedContext(_, ref gl) | - WindowWrapper::Angle(_, _, ref gl) | - WindowWrapper::Headless(_, ref gl) => &**gl, + WindowWrapper::WindowedContext(_, ref gl, _) | + WindowWrapper::Angle(_, _, ref gl, _) | + WindowWrapper::Headless(_, ref gl, _) => &**gl, } } + #[cfg(feature = "software")] + pub fn gl(&self) -> &dyn gl::Gl { + if let Some(swgl) = self.software_gl() { + swgl + } else { + self.native_gl() + } + } + + #[cfg(not(feature = "software"))] + pub fn gl(&self) -> &dyn gl::Gl { + self.native_gl() + } + pub fn clone_gl(&self) -> Rc { match *self { - WindowWrapper::WindowedContext(_, ref gl) | - WindowWrapper::Angle(_, _, ref gl) | - WindowWrapper::Headless(_, ref gl) => gl.clone(), + WindowWrapper::WindowedContext(_, ref gl, ref swgl) | + WindowWrapper::Angle(_, _, ref gl, ref swgl) | + WindowWrapper::Headless(_, ref gl, ref swgl) => { + match swgl { + #[cfg(feature = "software")] + Some(ref swgl) => Rc::new(swgl.clone()), + None => gl.clone(), + _ => panic!(), + } + } + } + } + + + #[cfg(feature = "software")] + fn update_software(&self, dim: DeviceIntSize) { + if let Some(swgl) = self.software_gl() { + swgl.init_default_framebuffer(dim.width, dim.height); } } + + #[cfg(not(feature = "software"))] + fn update_software(&self, _dim: DeviceIntSize) { + } + + fn update(&self, wrench: &mut Wrench) { + let dim = self.get_inner_size(); + self.update_software(dim); + wrench.update(dim); + } +} + +#[cfg(feature = "software")] +fn make_software_context() -> Option { + let ctx = swgl::Context::create(); + ctx.make_current(); + Some(ctx) +} + +#[cfg(not(feature = "software"))] +fn make_software_context() -> Option { + None } fn make_window( @@ -222,7 +321,14 @@ fn make_window( events_loop: &Option, angle: bool, gl_request: glutin::GlRequest, + software: bool, ) -> WindowWrapper { + let sw_ctx = if software { + make_software_context() + } else { + None + }; + let wrapper = match *events_loop { Some(ref events_loop) => { let context_builder = glutin::ContextBuilder::new() @@ -254,7 +360,7 @@ fn make_window( glutin::Api::WebGl => unimplemented!(), }; - WindowWrapper::Angle(_window, _context, gl) + WindowWrapper::Angle(_window, _context, gl, sw_ctx) } else { let windowed_context = context_builder .build_windowed(window_builder, events_loop) @@ -280,30 +386,39 @@ fn make_window( glutin::Api::WebGl => unimplemented!(), }; - WindowWrapper::WindowedContext(windowed_context, gl) + WindowWrapper::WindowedContext(windowed_context, gl, sw_ctx) } } None => { - let gl = match gl::GlType::default() { - gl::GlType::Gl => unsafe { - gl::GlFns::load_with(|symbol| { - HeadlessContext::get_proc_address(symbol) as *const _ - }) - }, - gl::GlType::Gles => unsafe { - gl::GlesFns::load_with(|symbol| { - HeadlessContext::get_proc_address(symbol) as *const _ - }) - }, + let gl = match sw_ctx { + #[cfg(feature = "software")] + Some(ref sw_ctx) => Rc::new(sw_ctx.clone()), + None => { + match gl::GlType::default() { + gl::GlType::Gl => unsafe { + gl::GlFns::load_with(|symbol| { + HeadlessContext::get_proc_address(symbol) as *const _ + }) + }, + gl::GlType::Gles => unsafe { + gl::GlesFns::load_with(|symbol| { + HeadlessContext::get_proc_address(symbol) as *const _ + }) + }, + } + } + _ => panic!(), }; - WindowWrapper::Headless(HeadlessContext::new(size.width, size.height), gl) + WindowWrapper::Headless(HeadlessContext::new(size.width, size.height), gl, sw_ctx) } }; - wrapper.gl().clear_color(0.3, 0.0, 0.0, 1.0); + let gl = wrapper.gl(); + + gl.clear_color(0.3, 0.0, 0.0, 1.0); - let gl_version = wrapper.gl().get_string(gl::VERSION); - let gl_renderer = wrapper.gl().get_string(gl::RENDERER); + let gl_version = gl.get_string(gl::VERSION); + let gl_renderer = gl.get_string(gl::RENDERER); let dp_ratio = dp_ratio.unwrap_or(wrapper.hidpi_factor()); println!("OpenGL version {}, {}", gl_version, gl_renderer); @@ -512,6 +627,8 @@ fn main() { } }; + let software = args.is_present("software"); + let mut window = make_window( size, dp_ratio, @@ -519,6 +636,7 @@ fn main() { &events_loop, args.is_present("angle"), gl_request, + software, ); let dp_ratio = dp_ratio.unwrap_or(window.hidpi_factor()); let dim = window.get_inner_size(); @@ -553,6 +671,7 @@ fn main() { dump_shader_source, notifier, ); + window.update(&mut wrench); if let Some(window_title) = wrench.take_title() { if !cfg!(windows) { @@ -648,8 +767,7 @@ fn render<'a>( let mut cpu_profile_index = 0; let mut cursor_position = WorldPoint::zero(); - let dim = window.get_inner_size(); - wrench.update(dim); + window.update(wrench); thing.do_frame(wrench); let mut debug_flags = DebugFlags::empty(); @@ -847,8 +965,7 @@ fn render<'a>( } } - let dim = window.get_inner_size(); - wrench.update(dim); + window.update(wrench); if do_frame { let frame_num = thing.do_frame(wrench); @@ -863,6 +980,7 @@ fn render<'a>( } wrench.render(); + window.upload_software_to_native(); window.swap_buffers(); if do_loop { From 9396fb26c080cf0e405862d9d33c6a9939a6cc72 Mon Sep 17 00:00:00 2001 From: Lee Salzman Date: Sat, 14 Mar 2020 10:36:10 +0000 Subject: [PATCH 07/23] Bug 1612941 - make WR report shader name and features in output for interop with SWGL. r=jrmuizel Differential Revision: https://phabricator.services.mozilla.com/D65595 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/558806ca76911093f504fb80232840f395f39d06 --- webrender/src/device/gl.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/webrender/src/device/gl.rs b/webrender/src/device/gl.rs index bf30978b13..55339804b0 100644 --- a/webrender/src/device/gl.rs +++ b/webrender/src/device/gl.rs @@ -269,8 +269,21 @@ fn build_shader_prefix_string( // GLSL requires that the version number comes first. output(gl_version_string); + let mut features_key = String::new(); + for feat in features.lines() { + const PREFIX: &'static str = "#define WR_FEATURE_"; + if let Some(i) = feat.find(PREFIX) { + if i + PREFIX.len() < feat.len() { + if !features_key.is_empty() { + features_key.push_str("_"); + } + features_key.push_str(&feat[i + PREFIX.len() ..]); + } + } + } + // Insert the shader name to make debugging easier. - let name_string = format!("// {}\n", base_filename); + let name_string = format!("// shader: {} {}\n", base_filename, features_key); output(&name_string); // Define a constant depending on whether we are compiling VS or FS. From 84bab7f5ce84d75a73fbd47232331d82f5f8d3b6 Mon Sep 17 00:00:00 2001 From: Lee Salzman Date: Sat, 14 Mar 2020 10:36:19 +0000 Subject: [PATCH 08/23] Bug 1612941 - properly detect GL_ARB_texture_swizzle extension in WR. r=gw,kvark Differential Revision: https://phabricator.services.mozilla.com/D65596 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/5097c1d4e3d7751134b60a8ecb775382a5f6ae44 --- webrender/src/device/gl.rs | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/webrender/src/device/gl.rs b/webrender/src/device/gl.rs index 55339804b0..3c16e7bc68 100644 --- a/webrender/src/device/gl.rs +++ b/webrender/src/device/gl.rs @@ -1412,13 +1412,19 @@ impl Device { let is_emulator = renderer_name.starts_with("Android Emulator"); let avoid_tex_image = is_emulator; + let supports_texture_storage = allow_texture_storage_support && + match gl.get_type() { + gl::GlType::Gl => supports_extension(&extensions, "GL_ARB_texture_storage"), + // ES 3 technically always supports glTexStorage, but only check here for the extension + // necessary to interact with BGRA. + gl::GlType::Gles => supports_extension(&extensions, "GL_EXT_texture_storage"), + }; + let supports_texture_swizzle = allow_texture_swizzling && + (gl.get_type() == gl::GlType::Gles || supports_extension(&extensions, "GL_ARB_texture_swizzle")); + let (color_formats, bgra_formats, bgra8_sampling_swizzle, texture_storage_usage) = match gl.get_type() { // There is `glTexStorage`, use it and expect RGBA on the input. - gl::GlType::Gl if - allow_texture_storage_support && - allow_texture_swizzling && - supports_extension(&extensions, "GL_ARB_texture_storage") - => ( + gl::GlType::Gl if supports_texture_storage && supports_texture_swizzle => ( TextureFormatPair::from(ImageFormat::RGBA8), TextureFormatPair { internal: gl::RGBA8, external: gl::RGBA }, Swizzle::Bgra, // pretend it's RGBA, rely on swizzling @@ -1432,7 +1438,7 @@ impl Device { TexStorageUsage::Never ), // We can use glTexStorage with BGRA8 as the internal format. - gl::GlType::Gles if supports_gles_bgra && allow_texture_storage_support && supports_extension(&extensions, "GL_EXT_texture_storage") => ( + gl::GlType::Gles if supports_gles_bgra && supports_texture_storage => ( TextureFormatPair::from(ImageFormat::BGRA8), TextureFormatPair { internal: gl::BGRA8_EXT, external: gl::BGRA_EXT }, Swizzle::Rgba, // no conversion needed @@ -1440,7 +1446,7 @@ impl Device { ), // For BGRA8 textures we must use the unsized BGRA internal // format and glTexImage. If texture storage is supported we can - // use it for other formats. + // use it for other formats, which is always the case for ES 3. // We can't use glTexStorage with BGRA8 as the internal format. gl::GlType::Gles if supports_gles_bgra && !avoid_tex_image => ( TextureFormatPair::from(ImageFormat::RGBA8), @@ -1450,7 +1456,7 @@ impl Device { ), // BGRA is not supported as an internal format, therefore we will // use RGBA. The swizzling will happen at the texture unit. - gl::GlType::Gles if allow_texture_swizzling => ( + gl::GlType::Gles if supports_texture_swizzle => ( TextureFormatPair::from(ImageFormat::RGBA8), TextureFormatPair { internal: gl::RGBA8, external: gl::RGBA }, Swizzle::Bgra, // pretend it's RGBA, rely on swizzling @@ -1494,10 +1500,6 @@ impl Device { supports_extension(&extensions, "GL_KHR_blend_equation_advanced") && !is_adreno; - let supports_texture_swizzle = allow_texture_swizzling && - (gl.get_type() == gl::GlType::Gles || supports_extension(&extensions, "GL_ARB_texture_storage")); - - // 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; From e83684ee153247d009636bf23f5d264d4721eac1 Mon Sep 17 00:00:00 2001 From: Lee Salzman Date: Sat, 14 Mar 2020 10:36:29 +0000 Subject: [PATCH 09/23] Bug 1612941 - only upload partial GPU cache rows in WR when possible to minimize memory churn. r=gw Differential Revision: https://phabricator.services.mozilla.com/D65598 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/a85061249e46c13dda65e808526a1a62a3a60dfb --- webrender/src/renderer.rs | 65 +++++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 19 deletions(-) diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index 5f1eb9f18d..71fb6529c2 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -1308,17 +1308,38 @@ struct CacheRow { /// Mirrored block data on CPU for this row. We store a copy of /// the data on the CPU side to improve upload batching. cpu_blocks: Box<[GpuBlockData; MAX_VERTEX_TEXTURE_WIDTH]>, - /// True if this row is dirty. - is_dirty: bool, + /// The first offset in this row that is dirty. + min_dirty: u16, + /// The last offset in this row that is dirty. + max_dirty: u16, } impl CacheRow { fn new() -> Self { CacheRow { cpu_blocks: Box::new([GpuBlockData::EMPTY; MAX_VERTEX_TEXTURE_WIDTH]), - is_dirty: false, + min_dirty: MAX_VERTEX_TEXTURE_WIDTH as _, + max_dirty: 0, } } + + fn is_dirty(&self) -> bool { + return self.min_dirty < self.max_dirty; + } + + fn clear_dirty(&mut self) { + self.min_dirty = MAX_VERTEX_TEXTURE_WIDTH as _; + self.max_dirty = 0; + } + + fn add_dirty(&mut self, block_offset: usize, block_count: usize) { + self.min_dirty = self.min_dirty.min(block_offset as _); + self.max_dirty = self.max_dirty.max((block_offset + block_count) as _); + } + + fn dirty_blocks(&self) -> &[GpuBlockData] { + return &self.cpu_blocks[self.min_dirty as usize .. self.max_dirty as usize]; + } } /// The bus over which CPU and GPU versions of the GPU cache @@ -1506,15 +1527,15 @@ impl GpuCacheTexture { rows.push(CacheRow::new()); } - // This row is dirty (needs to be updated in GPU texture). - rows[row].is_dirty = true; - // Copy the blocks from the patch array in the shadow CPU copy. let block_offset = address.u as usize; let data = &mut rows[row].cpu_blocks; for i in 0 .. block_count { data[block_offset + i] = updates.blocks[block_index + i]; } + + // This row is dirty (needs to be updated in GPU texture). + rows[row].add_dirty(block_offset, block_count); } } } @@ -1561,7 +1582,7 @@ impl GpuCacheTexture { GpuCacheBus::PixelBuffer { ref buffer, ref mut rows } => { let rows_dirty = rows .iter() - .filter(|row| row.is_dirty) + .filter(|row| row.is_dirty()) .count(); if rows_dirty == 0 { return 0 @@ -1579,18 +1600,19 @@ impl GpuCacheTexture { ); for (row_index, row) in rows.iter_mut().enumerate() { - if !row.is_dirty { + if !row.is_dirty() { continue; } + let blocks = row.dirty_blocks(); let rect = DeviceIntRect::new( - DeviceIntPoint::new(0, row_index as i32), - DeviceIntSize::new(MAX_VERTEX_TEXTURE_WIDTH as i32, 1), + DeviceIntPoint::new(row.min_dirty as i32, row_index as i32), + DeviceIntSize::new(blocks.len() as i32, 1), ); - uploader.upload(rect, 0, None, None, row.cpu_blocks.as_ptr(), row.cpu_blocks.len()); + uploader.upload(rect, 0, None, None, blocks.as_ptr(), blocks.len()); - row.is_dirty = false; + row.clear_dirty(); } rows_dirty @@ -1707,12 +1729,15 @@ impl VertexDataTexture { // (like Intel iGPUs) that prefers power-of-two sizes of textures ([1]). // // [1] https://software.intel.com/en-us/articles/opengl-performance-tips-power-of-two-textures-have-better-performance - let logical_width = - (MAX_VERTEX_TEXTURE_WIDTH - (MAX_VERTEX_TEXTURE_WIDTH % texels_per_item)) as i32; + let logical_width = if needed_height == 1 { + data.len() * texels_per_item + } else { + MAX_VERTEX_TEXTURE_WIDTH - (MAX_VERTEX_TEXTURE_WIDTH % texels_per_item) + }; let rect = DeviceIntRect::new( DeviceIntPoint::zero(), - DeviceIntSize::new(logical_width, needed_height), + DeviceIntSize::new(logical_width as i32, needed_height), ); debug_assert!(len <= data.capacity(), "CPU copy will read out of bounds"); @@ -1720,9 +1745,11 @@ impl VertexDataTexture { rect.size, self.texture().get_format(), ); - device - .upload_texture(self.texture(), &self.pbo, upload_size) - .upload(rect, 0, None, None, data.as_ptr(), len); + if upload_size > 0 { + device + .upload_texture(self.texture(), &self.pbo, upload_size) + .upload(rect, 0, None, None, data.as_ptr(), len); + } } fn deinit(mut self, device: &mut Device) { @@ -2960,7 +2987,7 @@ impl Renderer { GpuCacheBus::PixelBuffer { ref mut rows, .. } => { info!("Invalidating GPU caches"); for row in rows { - row.is_dirty = true; + row.add_dirty(0, MAX_VERTEX_TEXTURE_WIDTH); } } GpuCacheBus::Scatter { .. } => { From fde0ad2676a27b1dae42ea1622975c96a11a5fad Mon Sep 17 00:00:00 2001 From: Lee Salzman Date: Sat, 14 Mar 2020 10:36:38 +0000 Subject: [PATCH 10/23] Bug 1612941 - support variable depth buffer size in WR. r=gw Differential Revision: https://phabricator.services.mozilla.com/D65602 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/b6ea5a6b0a721defdc7e9aa2a7af54897ad1a0e2 --- webrender/src/composite.rs | 3 ++- webrender/src/debug_render.rs | 6 ++--- webrender/src/device/gl.rs | 42 ++++++++++++++++++++++++++++++--- webrender/src/frame_builder.rs | 4 +++- webrender/src/gpu_types.rs | 16 ++++++------- webrender/src/internal_types.rs | 5 ---- webrender/src/renderer.rs | 27 +++++++++++---------- webrender/src/scene.rs | 1 + 8 files changed, 69 insertions(+), 35 deletions(-) diff --git a/webrender/src/composite.rs b/webrender/src/composite.rs index 20b6244926..1e82c592cf 100644 --- a/webrender/src/composite.rs +++ b/webrender/src/composite.rs @@ -322,6 +322,7 @@ impl CompositeState { compositor_kind: CompositorKind, mut picture_caching_is_enabled: bool, global_device_pixel_scale: DevicePixelScale, + max_depth_ids: i32, ) -> Self { // The native compositor interface requires picture caching to work, so // force it here and warn if it was disabled. @@ -336,7 +337,7 @@ impl CompositeState { opaque_tiles: Vec::new(), alpha_tiles: Vec::new(), clear_tiles: Vec::new(), - z_generator: ZBufferIdGenerator::new(0), + z_generator: ZBufferIdGenerator::new(0, max_depth_ids), dirty_rects_are_valid: true, compositor_kind, picture_caching_is_enabled, diff --git a/webrender/src/debug_render.rs b/webrender/src/debug_render.rs index 42cf43bf05..1be03c5acf 100644 --- a/webrender/src/debug_render.rs +++ b/webrender/src/debug_render.rs @@ -8,7 +8,7 @@ use crate::debug_font_data; use crate::device::{Device, Program, Texture, TextureSlot, VertexDescriptor, ShaderError, VAO}; use crate::device::{TextureFilter, VertexAttribute, VertexAttributeKind, VertexUsageHint}; use euclid::{Point2D, Rect, Size2D, Transform3D, default}; -use crate::internal_types::{ORTHO_FAR_PLANE, ORTHO_NEAR_PLANE, Swizzle}; +use crate::internal_types::Swizzle; use std::f32; #[cfg_attr(feature = "capture", derive(Serialize))] @@ -333,8 +333,8 @@ impl DebugRenderer { viewport_size.width as f32 * scale, bottom, top, - ORTHO_NEAR_PLANE, - ORTHO_FAR_PLANE, + device.ortho_near_plane(), + device.ortho_far_plane(), ); // Triangles diff --git a/webrender/src/device/gl.rs b/webrender/src/device/gl.rs index 3c16e7bc68..b850dab61a 100644 --- a/webrender/src/device/gl.rs +++ b/webrender/src/device/gl.rs @@ -1034,6 +1034,11 @@ enum TexStorageUsage { Always, } +// We get 24 bits of Z value - use up 22 bits of it to give us +// 4 bits to account for GPU issues. This seems to manifest on +// some GPUs under certain perspectives due to z interpolation +// precision problems. +const RESERVE_DEPTH_BITS: i32 = 2; pub struct Device { gl: Rc, @@ -1064,6 +1069,7 @@ pub struct Device { color_formats: TextureFormatPair, bgra_formats: TextureFormatPair, swizzle_settings: SwizzleSettings, + depth_format: gl::GLuint, /// Map from texture dimensions to shared depth buffers for render targets. /// @@ -1471,8 +1477,14 @@ impl Device { ), }; - info!("GL texture cache {:?}, bgra {:?} swizzle {:?}, texture storage {:?}", - color_formats, bgra_formats, bgra8_sampling_swizzle, texture_storage_usage); + let depth_format = if renderer_name.starts_with("Software WebRender") { + gl::DEPTH_COMPONENT16 + } else { + gl::DEPTH_COMPONENT24 + }; + + info!("GL texture cache {:?}, bgra {:?} swizzle {:?}, texture storage {:?}, depth {:?}", + color_formats, bgra_formats, bgra8_sampling_swizzle, texture_storage_usage, depth_format); let supports_copy_image_sub_data = supports_extension(&extensions, "GL_EXT_copy_image") || supports_extension(&extensions, "GL_ARB_copy_image"); @@ -1544,6 +1556,7 @@ impl Device { swizzle_settings: SwizzleSettings { bgra8_sampling_swizzle, }, + depth_format, depth_targets: FastHashMap::default(), @@ -1624,6 +1637,28 @@ impl Device { } } + pub fn depth_bits(&self) -> i32 { + match self.depth_format { + gl::DEPTH_COMPONENT16 => 16, + gl::DEPTH_COMPONENT24 => 24, + _ => panic!("Unknown depth format {:?}", self.depth_format), + } + } + + // See gpu_types.rs where we declare the number of possible documents and + // number of items per document. This should match up with that. + pub fn max_depth_ids(&self) -> i32 { + return 1 << (self.depth_bits() - RESERVE_DEPTH_BITS); + } + + pub fn ortho_near_plane(&self) -> f32 { + return -self.max_depth_ids() as f32; + } + + pub fn ortho_far_plane(&self) -> f32 { + return (self.max_depth_ids() - 1) as f32; + } + pub fn optimal_pbo_stride(&self) -> NonZeroUsize { self.optimal_pbo_stride } @@ -2420,13 +2455,14 @@ impl Device { fn acquire_depth_target(&mut self, dimensions: DeviceIntSize) -> RBOId { let gl = &self.gl; + let depth_format = self.depth_format; let target = self.depth_targets.entry(dimensions).or_insert_with(|| { let renderbuffer_ids = gl.gen_renderbuffers(1); let depth_rb = renderbuffer_ids[0]; gl.bind_renderbuffer(gl::RENDERBUFFER, depth_rb); gl.renderbuffer_storage( gl::RENDERBUFFER, - gl::DEPTH_COMPONENT24, + depth_format, dimensions.width as _, dimensions.height as _, ); diff --git a/webrender/src/frame_builder.rs b/webrender/src/frame_builder.rs index deb2c9a5c3..0a272c7550 100644 --- a/webrender/src/frame_builder.rs +++ b/webrender/src/frame_builder.rs @@ -66,6 +66,7 @@ pub struct FrameBuilderConfig { pub background_color: Option, pub compositor_kind: CompositorKind, pub tile_size_override: Option, + pub max_depth_ids: i32, } /// A set of common / global resources that are retained between @@ -569,6 +570,7 @@ impl FrameBuilder { scene.config.compositor_kind, picture_caching_is_enabled, global_device_pixel_scale, + scene.config.max_depth_ids, ); let main_render_task_id = self.build_layer_screen_rects_and_cull_layers( @@ -605,7 +607,7 @@ impl FrameBuilder { ); // Used to generated a unique z-buffer value per primitive. - let mut z_generator = ZBufferIdGenerator::new(layer); + let mut z_generator = ZBufferIdGenerator::new(layer, scene.config.max_depth_ids); let use_dual_source_blending = scene.config.dual_source_blending_is_enabled && scene.config.dual_source_blending_is_supported; diff --git a/webrender/src/gpu_types.rs b/webrender/src/gpu_types.rs index 57b9042a9a..067ba52551 100644 --- a/webrender/src/gpu_types.rs +++ b/webrender/src/gpu_types.rs @@ -25,12 +25,7 @@ pub const VECS_PER_TRANSFORM: usize = 8; #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct ZBufferId(pub i32); -// We get 24 bits of Z value - use up 22 bits of it to give us -// 4 bits to account for GPU issues. This seems to manifest on -// some GPUs under certain perspectives due to z interpolation -// precision problems. const MAX_DOCUMENT_LAYERS : i8 = 1 << 3; -const MAX_ITEMS_PER_DOCUMENT_LAYER : i32 = 1 << 19; const MAX_DOCUMENT_LAYER_VALUE : i8 = MAX_DOCUMENT_LAYERS / 2 - 1; const MIN_DOCUMENT_LAYER_VALUE : i8 = -MAX_DOCUMENT_LAYERS / 2; @@ -46,20 +41,23 @@ impl ZBufferId { pub struct ZBufferIdGenerator { base: i32, next: i32, + max_items_per_document_layer: i32, } impl ZBufferIdGenerator { - pub fn new(layer: DocumentLayer) -> Self { + pub fn new(layer: DocumentLayer, max_depth_ids: i32) -> Self { debug_assert!(layer >= MIN_DOCUMENT_LAYER_VALUE); debug_assert!(layer <= MAX_DOCUMENT_LAYER_VALUE); + let max_items_per_document_layer = max_depth_ids / MAX_DOCUMENT_LAYERS as i32; ZBufferIdGenerator { - base: layer as i32 * MAX_ITEMS_PER_DOCUMENT_LAYER, - next: 0 + base: layer as i32 * max_items_per_document_layer, + next: 0, + max_items_per_document_layer, } } pub fn next(&mut self) -> ZBufferId { - debug_assert!(self.next < MAX_ITEMS_PER_DOCUMENT_LAYER); + debug_assert!(self.next < self.max_items_per_document_layer); let id = ZBufferId(self.next + self.base); self.next += 1; id diff --git a/webrender/src/internal_types.rs b/webrender/src/internal_types.rs index 1e8412bd69..a7f9996a07 100644 --- a/webrender/src/internal_types.rs +++ b/webrender/src/internal_types.rs @@ -299,11 +299,6 @@ pub enum TextureSource { Dummy, } -// See gpu_types.rs where we declare the number of possible documents and -// number of items per document. This should match up with that. -pub const ORTHO_NEAR_PLANE: f32 = -(1 << 22) as f32; -pub const ORTHO_FAR_PLANE: f32 = ((1 << 22) - 1) as f32; - #[derive(Copy, Clone, Debug, PartialEq)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index 71fb6529c2..0d0c71862e 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -69,7 +69,7 @@ use crate::gpu_cache::{GpuBlockData, GpuCacheUpdate, GpuCacheUpdateList}; use crate::gpu_cache::{GpuCacheDebugChunk, GpuCacheDebugCmd}; use crate::gpu_types::{PrimitiveHeaderI, PrimitiveHeaderF, ScalingInstance, SvgFilterInstance, TransformData}; use crate::gpu_types::{CompositeInstance, ResolveInstanceData, ZBufferId}; -use crate::internal_types::{TextureSource, ORTHO_FAR_PLANE, ORTHO_NEAR_PLANE, ResourceCacheError}; +use crate::internal_types::{TextureSource, ResourceCacheError}; use crate::internal_types::{CacheTextureId, DebugOutput, FastHashMap, FastHashSet, LayerIndex, RenderedDocument, ResultMsg}; use crate::internal_types::{TextureCacheAllocationKind, TextureCacheUpdate, TextureUpdateList, TextureUpdateSource}; use crate::internal_types::{RenderTargetInfo, SavedTargetIndex, Swizzle}; @@ -2268,6 +2268,7 @@ impl Renderer { background_color: options.clear_color, compositor_kind, tile_size_override: None, + max_depth_ids: device.max_depth_ids(), }; info!("WR {:?}", config); @@ -4341,8 +4342,8 @@ impl Renderer { surface_size.width as f32, 0.0, surface_size.height as f32, - ORTHO_NEAR_PLANE, - ORTHO_FAR_PLANE, + self.device.ortho_near_plane(), + self.device.ortho_far_plane(), ); // Bind an appropriate YUV shader for the texture format kind @@ -5124,8 +5125,8 @@ impl Renderer { target_size.width as f32, 0.0, target_size.height as f32, - ORTHO_NEAR_PLANE, - ORTHO_FAR_PLANE, + self.device.ortho_near_plane(), + self.device.ortho_far_plane(), ) }; @@ -5586,8 +5587,8 @@ impl Renderer { offset.x + size.width, bottom, top, - ORTHO_NEAR_PLANE, - ORTHO_FAR_PLANE, + self.device.ortho_near_plane(), + self.device.ortho_far_plane(), ); let fb_scale = Scale::<_, _, FramebufferPixel>::new(1i32); @@ -5724,8 +5725,8 @@ impl Renderer { draw_target.dimensions().width as f32, 0.0, draw_target.dimensions().height as f32, - ORTHO_NEAR_PLANE, - ORTHO_FAR_PLANE, + self.device.ortho_near_plane(), + self.device.ortho_far_plane(), ); self.draw_picture_cache_target( @@ -5765,8 +5766,8 @@ impl Renderer { draw_target.dimensions().width as f32, 0.0, draw_target.dimensions().height as f32, - ORTHO_NEAR_PLANE, - ORTHO_FAR_PLANE, + self.device.ortho_near_plane(), + self.device.ortho_far_plane(), ); self.draw_alpha_target( @@ -5791,8 +5792,8 @@ impl Renderer { draw_target.dimensions().width as f32, 0.0, draw_target.dimensions().height as f32, - ORTHO_NEAR_PLANE, - ORTHO_FAR_PLANE, + self.device.ortho_near_plane(), + self.device.ortho_far_plane(), ); let clear_depth = if target.needs_depth() { diff --git a/webrender/src/scene.rs b/webrender/src/scene.rs index 0416ec8f68..0f468b494f 100644 --- a/webrender/src/scene.rs +++ b/webrender/src/scene.rs @@ -296,6 +296,7 @@ impl BuiltScene { background_color: None, compositor_kind: CompositorKind::default(), tile_size_override: None, + max_depth_ids: 0, }, } } From bac5c579daa66b48609e034e86566e345df50bd4 Mon Sep 17 00:00:00 2001 From: Lee Salzman Date: Sat, 14 Mar 2020 10:36:48 +0000 Subject: [PATCH 11/23] Bug 1612941 - force immediate upload method for SWGL since PBOs are slower there. r=jrmuizel Differential Revision: https://phabricator.services.mozilla.com/D65613 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/39adb08e0acafdf063aff479c29b9b9462555ac8 --- webrender/src/device/gl.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/webrender/src/device/gl.rs b/webrender/src/device/gl.rs index b850dab61a..0638e4c68e 100644 --- a/webrender/src/device/gl.rs +++ b/webrender/src/device/gl.rs @@ -1477,10 +1477,10 @@ impl Device { ), }; - let depth_format = if renderer_name.starts_with("Software WebRender") { - gl::DEPTH_COMPONENT16 + let (depth_format, upload_method) = if renderer_name.starts_with("Software WebRender") { + (gl::DEPTH_COMPONENT16, UploadMethod::Immediate) } else { - gl::DEPTH_COMPONENT24 + (gl::DEPTH_COMPONENT24, upload_method) }; info!("GL texture cache {:?}, bgra {:?} swizzle {:?}, texture storage {:?}, depth {:?}", From d7d392739750e2c56079ecb4bbdee41e00f913ce Mon Sep 17 00:00:00 2001 From: Lee Salzman Date: Sat, 14 Mar 2020 10:36:59 +0000 Subject: [PATCH 12/23] Bug 1612941 - WR shader changes to support SWGL instance attribs. r=jrmuizel,gw Add annotations to vertex shaders so that SWGL can detect when a vertex attribute is generated by per-instance data rather than per-vertex data. Differential Revision: https://phabricator.services.mozilla.com/D65614 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/cf730c4e65393b07f842ab6f58c2509a1009f920 --- webrender/res/base.glsl | 8 ++++++++ webrender/res/clip_shared.glsl | 14 +++++++------- webrender/res/composite.glsl | 16 ++++++++-------- webrender/res/cs_blur.glsl | 6 +++--- webrender/res/cs_border_segment.glsl | 18 +++++++++--------- webrender/res/cs_border_solid.glsl | 18 +++++++++--------- webrender/res/cs_gradient.glsl | 16 ++++++++-------- webrender/res/cs_line_decoration.glsl | 10 +++++----- webrender/res/cs_scale.glsl | 6 +++--- webrender/res/cs_svg_filter.glsl | 14 +++++++------- webrender/res/debug_color.glsl | 2 +- webrender/res/debug_font.glsl | 2 +- webrender/res/pf_vector_cover.glsl | 8 ++++---- webrender/res/pf_vector_stencil.glsl | 16 ++++++++-------- webrender/res/pls_init.glsl | 2 +- webrender/res/pls_resolve.glsl | 2 +- webrender/res/prim_shared.glsl | 2 +- webrender/res/shared.glsl | 2 +- 18 files changed, 85 insertions(+), 77 deletions(-) diff --git a/webrender/res/base.glsl b/webrender/res/base.glsl index 9d66a064a9..eb343c019b 100644 --- a/webrender/res/base.glsl +++ b/webrender/res/base.glsl @@ -36,6 +36,14 @@ #endif #ifdef WR_VERTEX_SHADER + #ifdef SWGL + // Annotate a vertex attribute as being flat per each drawn primitive instance. + // SWGL can use this information to avoid redundantly loading the attribute in all SIMD lanes. + #define PER_INSTANCE flat + #else + #define PER_INSTANCE + #endif + #define varying out #endif diff --git a/webrender/res/clip_shared.glsl b/webrender/res/clip_shared.glsl index a6a7d2dd50..e5b8038968 100644 --- a/webrender/res/clip_shared.glsl +++ b/webrender/res/clip_shared.glsl @@ -6,13 +6,13 @@ #ifdef WR_VERTEX_SHADER -in ivec2 aTransformIds; -in ivec4 aClipDataResourceAddress; -in vec2 aClipLocalPos; -in vec4 aClipTileRect; -in vec4 aClipDeviceArea; -in vec4 aClipOrigins; -in float aDevicePixelScale; +PER_INSTANCE in ivec2 aTransformIds; +PER_INSTANCE in ivec4 aClipDataResourceAddress; +PER_INSTANCE in vec2 aClipLocalPos; +PER_INSTANCE in vec4 aClipTileRect; +PER_INSTANCE in vec4 aClipDeviceArea; +PER_INSTANCE in vec4 aClipOrigins; +PER_INSTANCE in float aDevicePixelScale; struct ClipMaskInstance { int clip_transform_id; diff --git a/webrender/res/composite.glsl b/webrender/res/composite.glsl index a63df80e02..81b4454fab 100644 --- a/webrender/res/composite.glsl +++ b/webrender/res/composite.glsl @@ -23,16 +23,16 @@ varying vec2 vUv; #endif #ifdef WR_VERTEX_SHADER -in vec4 aDeviceRect; -in vec4 aDeviceClipRect; -in vec4 aColor; -in vec4 aParams; -in vec3 aTextureLayers; +PER_INSTANCE in vec4 aDeviceRect; +PER_INSTANCE in vec4 aDeviceClipRect; +PER_INSTANCE in vec4 aColor; +PER_INSTANCE in vec4 aParams; +PER_INSTANCE in vec3 aTextureLayers; #ifdef WR_FEATURE_YUV -in vec4 aUvRect0; -in vec4 aUvRect1; -in vec4 aUvRect2; +PER_INSTANCE in vec4 aUvRect0; +PER_INSTANCE in vec4 aUvRect1; +PER_INSTANCE in vec4 aUvRect2; #endif void main(void) { diff --git a/webrender/res/cs_blur.glsl b/webrender/res/cs_blur.glsl index db3583210b..a10fe00d79 100644 --- a/webrender/res/cs_blur.glsl +++ b/webrender/res/cs_blur.glsl @@ -18,9 +18,9 @@ flat varying int vSupport; #define DIR_HORIZONTAL 0 #define DIR_VERTICAL 1 -in int aBlurRenderTaskAddress; -in int aBlurSourceTaskAddress; -in int aBlurDirection; +PER_INSTANCE in int aBlurRenderTaskAddress; +PER_INSTANCE in int aBlurSourceTaskAddress; +PER_INSTANCE in int aBlurDirection; struct BlurTask { RenderTaskCommonData common_data; diff --git a/webrender/res/cs_border_segment.glsl b/webrender/res/cs_border_segment.glsl index 0b833d278a..9eb5155f1f 100644 --- a/webrender/res/cs_border_segment.glsl +++ b/webrender/res/cs_border_segment.glsl @@ -72,15 +72,15 @@ varying vec2 vPos; #ifdef WR_VERTEX_SHADER -in vec2 aTaskOrigin; -in vec4 aRect; -in vec4 aColor0; -in vec4 aColor1; -in int aFlags; -in vec2 aWidths; -in vec2 aRadii; -in vec4 aClipParams1; -in vec4 aClipParams2; +PER_INSTANCE in vec2 aTaskOrigin; +PER_INSTANCE in vec4 aRect; +PER_INSTANCE in vec4 aColor0; +PER_INSTANCE in vec4 aColor1; +PER_INSTANCE in int aFlags; +PER_INSTANCE in vec2 aWidths; +PER_INSTANCE in vec2 aRadii; +PER_INSTANCE in vec4 aClipParams1; +PER_INSTANCE in vec4 aClipParams2; vec2 get_outer_corner_scale(int segment) { vec2 p; diff --git a/webrender/res/cs_border_solid.glsl b/webrender/res/cs_border_solid.glsl index dde00b83ec..d574814353 100644 --- a/webrender/res/cs_border_solid.glsl +++ b/webrender/res/cs_border_solid.glsl @@ -45,15 +45,15 @@ varying vec2 vPos; #ifdef WR_VERTEX_SHADER -in vec2 aTaskOrigin; -in vec4 aRect; -in vec4 aColor0; -in vec4 aColor1; -in int aFlags; -in vec2 aWidths; -in vec2 aRadii; -in vec4 aClipParams1; -in vec4 aClipParams2; +PER_INSTANCE in vec2 aTaskOrigin; +PER_INSTANCE in vec4 aRect; +PER_INSTANCE in vec4 aColor0; +PER_INSTANCE in vec4 aColor1; +PER_INSTANCE in int aFlags; +PER_INSTANCE in vec2 aWidths; +PER_INSTANCE in vec2 aRadii; +PER_INSTANCE in vec4 aClipParams1; +PER_INSTANCE in vec4 aClipParams2; vec2 get_outer_corner_scale(int segment) { vec2 p; diff --git a/webrender/res/cs_gradient.glsl b/webrender/res/cs_gradient.glsl index 6e848a34dc..6e6b5c4ad0 100644 --- a/webrender/res/cs_gradient.glsl +++ b/webrender/res/cs_gradient.glsl @@ -13,14 +13,14 @@ flat varying vec4 vColor3; #ifdef WR_VERTEX_SHADER -in vec4 aTaskRect; -in float aAxisSelect; -in vec4 aStops; -in vec4 aColor0; -in vec4 aColor1; -in vec4 aColor2; -in vec4 aColor3; -in vec2 aStartStop; +PER_INSTANCE in vec4 aTaskRect; +PER_INSTANCE in float aAxisSelect; +PER_INSTANCE in vec4 aStops; +PER_INSTANCE in vec4 aColor0; +PER_INSTANCE in vec4 aColor1; +PER_INSTANCE in vec4 aColor2; +PER_INSTANCE in vec4 aColor3; +PER_INSTANCE in vec2 aStartStop; void main(void) { vPos = mix(aStartStop.x, aStartStop.y, mix(aPosition.x, aPosition.y, aAxisSelect)); diff --git a/webrender/res/cs_line_decoration.glsl b/webrender/res/cs_line_decoration.glsl index 42336fcc11..7063bcffb1 100644 --- a/webrender/res/cs_line_decoration.glsl +++ b/webrender/res/cs_line_decoration.glsl @@ -21,22 +21,22 @@ flat varying vec4 vParams; #ifdef WR_VERTEX_SHADER // The size of the mask tile we're rendering, in pixels. -in vec4 aTaskRect; +PER_INSTANCE in vec4 aTaskRect; // The size of the mask tile. aLocalSize.x is always horizontal and .y vertical, // regardless of the line's orientation. The size is chosen by // prim_store::get_line_decoration_sizes. -in vec2 aLocalSize; +PER_INSTANCE in vec2 aLocalSize; // A LINE_STYLE_* value, indicating what sort of line to draw. -in int aStyle; +PER_INSTANCE in int aStyle; // 0.0 for a horizontal line, 1.0 for a vertical line. -in float aAxisSelect; +PER_INSTANCE in float aAxisSelect; // The thickness of the wavy line itself, not the amplitude of the waves (i.e., // the thickness of the final decorated line). -in float aWavyLineThickness; +PER_INSTANCE in float aWavyLineThickness; void main(void) { vec2 size = mix(aLocalSize, aLocalSize.yx, aAxisSelect); diff --git a/webrender/res/cs_scale.glsl b/webrender/res/cs_scale.glsl index 183e6774cc..6d0c0e5998 100644 --- a/webrender/res/cs_scale.glsl +++ b/webrender/res/cs_scale.glsl @@ -9,9 +9,9 @@ flat varying vec4 vUvRect; #ifdef WR_VERTEX_SHADER -in vec4 aScaleTargetRect; -in ivec4 aScaleSourceRect; -in int aScaleSourceLayer; +PER_INSTANCE in vec4 aScaleTargetRect; +PER_INSTANCE in ivec4 aScaleSourceRect; +PER_INSTANCE in int aScaleSourceLayer; void main(void) { RectWithSize src_rect = RectWithSize(vec2(aScaleSourceRect.xy), vec2(aScaleSourceRect.zw)); diff --git a/webrender/res/cs_svg_filter.glsl b/webrender/res/cs_svg_filter.glsl index b43ca2ecf9..62d808a1e3 100644 --- a/webrender/res/cs_svg_filter.glsl +++ b/webrender/res/cs_svg_filter.glsl @@ -39,13 +39,13 @@ flat varying int vFuncs[4]; #ifdef WR_VERTEX_SHADER -in int aFilterRenderTaskAddress; -in int aFilterInput1TaskAddress; -in int aFilterInput2TaskAddress; -in int aFilterKind; -in int aFilterInputCount; -in int aFilterGenericInt; -in ivec2 aFilterExtraDataAddress; +PER_INSTANCE in int aFilterRenderTaskAddress; +PER_INSTANCE in int aFilterInput1TaskAddress; +PER_INSTANCE in int aFilterInput2TaskAddress; +PER_INSTANCE in int aFilterKind; +PER_INSTANCE in int aFilterInputCount; +PER_INSTANCE in int aFilterGenericInt; +PER_INSTANCE in ivec2 aFilterExtraDataAddress; struct FilterTask { RenderTaskCommonData common_data; diff --git a/webrender/res/debug_color.glsl b/webrender/res/debug_color.glsl index 15285bdbbe..b5a636e535 100644 --- a/webrender/res/debug_color.glsl +++ b/webrender/res/debug_color.glsl @@ -11,7 +11,7 @@ in vec4 aColor; void main(void) { vColor = vec4(aColor.rgb * aColor.a, aColor.a); - vec4 pos = vec4(aPosition, 1.0); + vec4 pos = vec4(aPosition, 0.0, 1.0); pos.xy = floor(pos.xy + 0.5); gl_Position = uTransform * pos; } diff --git a/webrender/res/debug_font.glsl b/webrender/res/debug_font.glsl index b93c4bcaa5..5b9fb2109f 100644 --- a/webrender/res/debug_font.glsl +++ b/webrender/res/debug_font.glsl @@ -14,7 +14,7 @@ in vec2 aColorTexCoord; void main(void) { vColor = aColor; vColorTexCoord = aColorTexCoord; - vec4 pos = vec4(aPosition, 1.0); + vec4 pos = vec4(aPosition, 0.0, 1.0); pos.xy = floor(pos.xy + 0.5); gl_Position = uTransform * pos; } diff --git a/webrender/res/pf_vector_cover.glsl b/webrender/res/pf_vector_cover.glsl index a4eece1a1f..1b2eeabb7d 100644 --- a/webrender/res/pf_vector_cover.glsl +++ b/webrender/res/pf_vector_cover.glsl @@ -6,10 +6,10 @@ #ifdef WR_VERTEX_SHADER -in ivec4 aTargetRect; -in ivec2 aStencilOrigin; -in int aSubpixel; -in int aPad; +PER_INSTANCE in ivec4 aTargetRect; +PER_INSTANCE in ivec2 aStencilOrigin; +PER_INSTANCE in int aSubpixel; +PER_INSTANCE in int aPad; out vec2 vStencilUV; flat out int vSubpixel; diff --git a/webrender/res/pf_vector_stencil.glsl b/webrender/res/pf_vector_stencil.glsl index e951eeba16..2029768fcb 100644 --- a/webrender/res/pf_vector_stencil.glsl +++ b/webrender/res/pf_vector_stencil.glsl @@ -6,14 +6,14 @@ #ifdef WR_VERTEX_SHADER -in vec2 aFromPosition; -in vec2 aCtrlPosition; -in vec2 aToPosition; -in vec2 aFromNormal; -in vec2 aCtrlNormal; -in vec2 aToNormal; -in int aPathID; -in int aPad; +PER_INSTANCE in vec2 aFromPosition; +PER_INSTANCE in vec2 aCtrlPosition; +PER_INSTANCE in vec2 aToPosition; +PER_INSTANCE in vec2 aFromNormal; +PER_INSTANCE in vec2 aCtrlNormal; +PER_INSTANCE in vec2 aToNormal; +PER_INSTANCE in int aPathID; +PER_INSTANCE in int aPad; out vec2 vFrom; out vec2 vCtrl; diff --git a/webrender/res/pls_init.glsl b/webrender/res/pls_init.glsl index 4d3e391a7e..a9fe0a6c4a 100644 --- a/webrender/res/pls_init.glsl +++ b/webrender/res/pls_init.glsl @@ -11,7 +11,7 @@ #include shared #ifdef WR_VERTEX_SHADER -in vec4 aRect; +PER_INSTANCE in vec4 aRect; void main(void) { vec2 pos = aRect.xy + aPosition.xy * aRect.zw; diff --git a/webrender/res/pls_resolve.glsl b/webrender/res/pls_resolve.glsl index 70ba5f1cca..363ce2ccfe 100644 --- a/webrender/res/pls_resolve.glsl +++ b/webrender/res/pls_resolve.glsl @@ -11,7 +11,7 @@ #include shared #ifdef WR_VERTEX_SHADER -in vec4 aRect; +PER_INSTANCE in vec4 aRect; void main(void) { vec2 pos = aRect.xy + aPosition.xy * aRect.zw; diff --git a/webrender/res/prim_shared.glsl b/webrender/res/prim_shared.glsl index d4a06b2a2d..f89340ff49 100644 --- a/webrender/res/prim_shared.glsl +++ b/webrender/res/prim_shared.glsl @@ -45,7 +45,7 @@ uniform HIGHP_SAMPLER_FLOAT sampler2D sPrimitiveHeadersF; uniform HIGHP_SAMPLER_FLOAT isampler2D sPrimitiveHeadersI; // Instanced attributes -in ivec4 aData; +PER_INSTANCE in ivec4 aData; #define VECS_PER_PRIM_HEADER_F 2U #define VECS_PER_PRIM_HEADER_I 2U diff --git a/webrender/res/shared.glsl b/webrender/res/shared.glsl index 0b375d66f4..7723b70f19 100644 --- a/webrender/res/shared.glsl +++ b/webrender/res/shared.glsl @@ -50,7 +50,7 @@ uniform mat4 uTransform; // Orthographic projection // Attribute inputs - in vec3 aPosition; + in vec2 aPosition; // get_fetch_uv is a macro to work around a macOS Intel driver parsing bug. // TODO: convert back to a function once the driver issues are resolved, if ever. From 830a0e3bc64f77135bbb0fe3bfbe3f648eb4a254 Mon Sep 17 00:00:00 2001 From: Lee Salzman Date: Sat, 14 Mar 2020 10:37:09 +0000 Subject: [PATCH 13/23] Bug 1612941 - update gleam version to allow implementation of Gl trait. r=jrmuizel Differential Revision: https://phabricator.services.mozilla.com/D65615 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/46024d1d85b3026c38f7336887a03ceae6875182 --- direct-composition/Cargo.toml | 2 +- example-compositor/compositor/Cargo.toml | 2 +- examples/Cargo.toml | 2 +- webrender/Cargo.toml | 2 +- wrench/Cargo.toml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/direct-composition/Cargo.toml b/direct-composition/Cargo.toml index 916a22349c..6d33a9fc10 100644 --- a/direct-composition/Cargo.toml +++ b/direct-composition/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" [target.'cfg(windows)'.dependencies] euclid = "0.20" -gleam = "0.9.2" +gleam = "0.10.0" mozangle = {version = "0.3.1", features = ["egl"]} webrender = {path = "../webrender"} winapi = {version = "0.3", features = ["winerror", "d3d11", "dcomp"]} diff --git a/example-compositor/compositor/Cargo.toml b/example-compositor/compositor/Cargo.toml index 31c9763435..f009be0e8a 100644 --- a/example-compositor/compositor/Cargo.toml +++ b/example-compositor/compositor/Cargo.toml @@ -7,7 +7,7 @@ license = "MPL-2.0" [dependencies] webrender = { path = "../../webrender" } -gleam = "0.9.2" +gleam = "0.10.0" [target.'cfg(windows)'.dependencies] compositor-windows = { path = "../compositor-windows" } diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 3052d6b7cf..f82c14fc37 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -61,7 +61,7 @@ debug = ["webrender/capture", "webrender/debugger", "webrender/profiler"] app_units = "0.7" env_logger = "0.5" euclid = "0.20" -gleam = "0.9.2" +gleam = "0.10.0" glutin = "0.21" rayon = "1" webrender = { path = "../webrender" } diff --git a/webrender/Cargo.toml b/webrender/Cargo.toml index 14677d16ad..98d13381b3 100644 --- a/webrender/Cargo.toml +++ b/webrender/Cargo.toml @@ -32,7 +32,7 @@ cfg-if = "0.1.2" cstr = "0.1.2" euclid = { version = "0.20.0", features = ["serde"] } fxhash = "0.2.1" -gleam = "0.9.2" +gleam = "0.10.0" image_loader = { optional = true, version = "0.23", package = "image", default-features = false, features = ["png"] } lazy_static = "1" log = "0.4" diff --git a/wrench/Cargo.toml b/wrench/Cargo.toml index bbc24264d6..5f16b129d0 100644 --- a/wrench/Cargo.toml +++ b/wrench/Cargo.toml @@ -12,7 +12,7 @@ bincode = "1.0" byteorder = "1.0" env_logger = { version = "0.5", optional = true } euclid = "0.20" -gleam = "0.9.2" +gleam = "0.10.0" glutin = "0.21" app_units = "0.7" clap = { version = "2", features = ["yaml"] } From 964e6c5b1e1b0a92cb0b9979c4e0fac7ef3d8564 Mon Sep 17 00:00:00 2001 From: Lee Salzman Date: Sat, 14 Mar 2020 10:37:18 +0000 Subject: [PATCH 14/23] Bug 1612941 - vendor SWGL's rust dependencies. r=froydnj Differential Revision: https://phabricator.services.mozilla.com/D65617 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/08e87bebb6062948732562a9f3e641563dd87390 --- Cargo.lock | 83 +- glsl-to-cxx/Cargo.toml | 11 + glsl-to-cxx/README.md | 3 + glsl-to-cxx/src/hir.rs | 3762 ++++++++++++++++++++++++++++++ glsl-to-cxx/src/lib.rs | 3689 +++++++++++++++++++++++++++++ glsl-to-cxx/src/main.rs | 8 + swgl/Cargo.toml | 14 + swgl/README.md | 4 + swgl/build.rs | 178 ++ swgl/src/gl.cc | 3114 +++++++++++++++++++++++++ swgl/src/gl_defs.h | 175 ++ swgl/src/glsl.h | 3053 ++++++++++++++++++++++++ swgl/src/lib.rs | 12 + swgl/src/program.h | 142 ++ swgl/src/swgl_fns.rs | 2217 ++++++++++++++++++ swgl/src/texture.h | 121 + swgl/src/vector_type.h | 473 ++++ webrender/res/brush_solid.frag.h | 15 + 18 files changed, 17058 insertions(+), 16 deletions(-) create mode 100644 glsl-to-cxx/Cargo.toml create mode 100644 glsl-to-cxx/README.md create mode 100644 glsl-to-cxx/src/hir.rs create mode 100644 glsl-to-cxx/src/lib.rs create mode 100644 glsl-to-cxx/src/main.rs create mode 100644 swgl/Cargo.toml create mode 100644 swgl/README.md create mode 100644 swgl/build.rs create mode 100644 swgl/src/gl.cc create mode 100644 swgl/src/gl_defs.h create mode 100644 swgl/src/glsl.h create mode 100644 swgl/src/lib.rs create mode 100644 swgl/src/program.h create mode 100644 swgl/src/swgl_fns.rs create mode 100644 swgl/src/texture.h create mode 100644 swgl/src/vector_type.h create mode 100644 webrender/res/brush_solid.frag.h diff --git a/Cargo.lock b/Cargo.lock index 4940067882..6b7719a205 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -96,7 +96,7 @@ name = "backtrace-sys" version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -177,10 +177,10 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.15" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rayon 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "jobserver 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -225,7 +225,7 @@ name = "cmake" version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -247,7 +247,7 @@ name = "compositor" version = "0.1.0" dependencies = [ "compositor-windows 0.1.0", - "gleam 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "webrender 0.61.0", ] @@ -255,7 +255,7 @@ dependencies = [ name = "compositor-windows" version = "0.1.0" dependencies = [ - "cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -420,7 +420,7 @@ name = "direct-composition" version = "0.1.0" dependencies = [ "euclid 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gleam 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "mozangle 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "webrender 0.61.0", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -617,12 +617,27 @@ dependencies = [ [[package]] name = "gleam" -version = "0.9.2" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "gl_generator 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "glsl" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nom 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "glsl-to-cxx" +version = "0.1.0" +dependencies = [ + "glsl 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "glutin" version = "0.21.0" @@ -747,6 +762,14 @@ name = "itoa" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "jobserver" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "kernel32-sys" version = "0.2.2" @@ -786,7 +809,7 @@ name = "libloading" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -907,7 +930,7 @@ name = "mozangle" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", "gl_generator 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 2.2.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -929,7 +952,7 @@ version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -940,6 +963,15 @@ name = "nodrop" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "nom" +version = "5.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "num-integer" version = "0.1.38" @@ -1503,6 +1535,15 @@ name = "svg_fmt" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "swgl" +version = "0.1.0" +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", +] + [[package]] name = "syn" version = "0.15.30" @@ -1674,6 +1715,11 @@ name = "vec_map" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "version_check" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "void" version = "1.0.2" @@ -1760,7 +1806,7 @@ dependencies = [ "euclid 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", "freetype 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gleam 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "image 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1793,7 +1839,7 @@ dependencies = [ "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gleam 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "glutin 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "webrender 0.61.0", @@ -1919,7 +1965,7 @@ dependencies = [ "env_logger 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", "font-loader 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gleam 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "glutin 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", "image 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1930,6 +1976,7 @@ dependencies = [ "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", + "swgl 0.1.0", "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", "webrender 0.61.0", "webrender_api 0.61.0", @@ -2020,7 +2067,7 @@ dependencies = [ "checksum bytemuck 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37fa13df2292ecb479ec23aa06f4507928bef07839be9ef15281411076629431" "checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" "checksum bytes 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "2f1d50c876fb7545f5f289cd8b2aee3f359d073ae819eed5d6373638e2c61e59" -"checksum cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)" = "0ebb87d1116151416c0cf66a0e3fb6430cccd120fd6300794b4dfaa050ac40ba" +"checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" "checksum cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "405216fd8fe65f718daa7102ea808a946b6ce40c742998fbfd3463645552de18" "checksum cgl 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "55e7ec0b74fe5897894cbc207092c577e87c52f8a59e8ca8d97ef37551f60a49" "checksum clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0f16b89cbb9ee36d87483dc939fe9f1e13c05898d56d7b230a0d4dff033a536" @@ -2066,8 +2113,9 @@ dependencies = [ "checksum gl_generator 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "39a23d5e872a275135d66895d954269cf5e8661d234eb1c2480f4ce0d586acbd" "checksum gl_generator 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ca98bbde17256e02d17336a6bdb5a50f7d0ccacee502e191d3e3d0ec2f96f84a" "checksum gl_generator 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +"checksum gleam 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "77b1fd76468fff720bad31016688b805c6442ebf2d7c29123e10e4984aa61986" "checksum gleam 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "cae10d7c99d0e77b4766e850a60898a17c1abaf01075531f1066f03dc7dc5fc5" -"checksum gleam 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "332d1f4e6c6181ed07178f84a552b2387d43ecf6821a86c22cfb3883ea3fb1b9" +"checksum glsl 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "766443890761b3c4edcce86cafaac97971b200662fbdd0446eb7c6b99b4401ea" "checksum glutin 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cb26027a84c3b9e1949ef0df0b6a3db8d0c124243a5c161ea25c7def90cb1474" "checksum glutin_egl_sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "23f48987ab6cb2b61ad903b59e54a2fd0c380a7baff68cffd6826b69a73dd326" "checksum glutin_emscripten_sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "245b3fdb08df6ffed7585365851f8404af9c7e2dd4b59f15262e968b6a95a0c7" @@ -2081,6 +2129,7 @@ dependencies = [ "checksum inflate 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4ec18d981200fd14e65ee8e35fb60ed1ce55227a02407303f3a72517c6146dcc" "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" "checksum itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c069bbec61e1ca5a596166e55dfe4773ff745c3d16b700013bcaff9a6df2c682" +"checksum jobserver 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum khronos_api 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" "checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" @@ -2105,6 +2154,7 @@ dependencies = [ "checksum net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)" = "9044faf1413a1057267be51b5afba8eb1090bd2231c693664aa1db716fe1eae0" "checksum nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce" "checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" +"checksum nom 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b471253da97532da4b61552249c521e01e736071f71c1a4f7ebbfbf0a06aad6" "checksum num-integer 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)" = "6ac0ea58d64a89d9d6b7688031b3be9358d6c919badcf7fbb0527ccfd891ee45" "checksum num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "af3fdbbc3291a5464dc57b03860ec37ca6bf915ed6ee385e7c6c052c422b2124" "checksum num-rational 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e96f040177bb3da242b5b1ecf3f54b5d5af3efbbfb18608977a5d2767b22f10" @@ -2192,6 +2242,7 @@ dependencies = [ "checksum user32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ef4711d107b21b410a3a974b1204d9accc8b10dad75d8324b5d755de1617d47" "checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737" "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" +"checksum version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum walkdir 2.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c7904a7e2bb3cdf0cf5e783f44204a85a37a93151738fa349f06680f59a98b45" "checksum wayland-client 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)" = "49963e5f9eeaf637bfcd1b9f0701c99fd5cd05225eb51035550d4272806f2713" diff --git a/glsl-to-cxx/Cargo.toml b/glsl-to-cxx/Cargo.toml new file mode 100644 index 0000000000..1ff4298b54 --- /dev/null +++ b/glsl-to-cxx/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "glsl-to-cxx" +version = "0.1.0" +license = "MPL-2.0" +authors = ["The Mozilla Project Developers"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +glsl = "4.0" diff --git a/glsl-to-cxx/README.md b/glsl-to-cxx/README.md new file mode 100644 index 0000000000..1b5b4845d2 --- /dev/null +++ b/glsl-to-cxx/README.md @@ -0,0 +1,3 @@ +A GLSL to C++ translator. + +Translates GLSL to vectorized C++. Intended for use with WebRender software backend. diff --git a/glsl-to-cxx/src/hir.rs b/glsl-to-cxx/src/hir.rs new file mode 100644 index 0000000000..f668c585bb --- /dev/null +++ b/glsl-to-cxx/src/hir.rs @@ -0,0 +1,3762 @@ +/* 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 glsl::syntax; +use glsl::syntax::{ArrayedIdentifier, ArraySpecifier, AssignmentOp, BinaryOp, Identifier}; +use glsl::syntax::{NonEmpty, PrecisionQualifier, StructFieldSpecifier, StructSpecifier}; +use glsl::syntax::{TypeSpecifier, TypeSpecifierNonArray, UnaryOp}; +use std::cell::{Cell, Ref, RefCell}; +use std::collections::HashMap; +use std::iter::FromIterator; +use std::mem; +use std::ops::{Deref, DerefMut}; +use std::rc::Rc; + +trait LiftFrom { + fn lift(state: &mut State, s: S) -> Self; +} + +fn lift>(state: &mut State, s: S) -> T { + LiftFrom::lift(state, s) +} + +#[derive(Debug)] +pub struct Symbol { + pub name: String, + pub decl: SymDecl, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct FunctionSignature { + ret: Type, + params: Vec, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct FunctionType { + signatures: NonEmpty, +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum SamplerFormat { + Unknown, + RGBA8, + RGBA32F, + RGBA32I, + R8, +} + +impl SamplerFormat { + pub fn type_suffix(self) -> Option<&'static str> { + match self { + SamplerFormat::Unknown => None, + SamplerFormat::RGBA8 => Some("RGBA8"), + SamplerFormat::RGBA32F => Some("RGBA32F"), + SamplerFormat::RGBA32I => Some("RGBA32I"), + SamplerFormat::R8 => Some("R8"), + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum StorageClass { + None, + Const, + In, + Out, + Uniform, + Sampler(SamplerFormat), + FragColor(i32), +} + +#[derive(Clone, Debug, PartialEq)] +pub struct ArraySizes { + pub sizes: Vec, +} + +impl LiftFrom<&ArraySpecifier> for ArraySizes { + fn lift(state: &mut State, a: &ArraySpecifier) -> Self { + ArraySizes { + sizes: vec![match a { + ArraySpecifier::Unsized => panic!(), + ArraySpecifier::ExplicitlySized(expr) => translate_expression(state, expr), + }], + } + } +} + +#[derive(Clone, Debug, PartialEq)] +pub enum TypeKind { + Void, + Bool, + Int, + UInt, + Float, + Double, + Vec2, + Vec3, + Vec4, + DVec2, + DVec3, + DVec4, + BVec2, + BVec3, + BVec4, + IVec2, + IVec3, + IVec4, + UVec2, + UVec3, + UVec4, + Mat2, + Mat3, + Mat4, + Mat23, + Mat24, + Mat32, + Mat34, + Mat42, + Mat43, + DMat2, + DMat3, + DMat4, + DMat23, + DMat24, + DMat32, + DMat34, + DMat42, + DMat43, + // floating point opaque types + Sampler1D, + Image1D, + Sampler2D, + Image2D, + Sampler3D, + Image3D, + SamplerCube, + ImageCube, + Sampler2DRect, + Image2DRect, + Sampler1DArray, + Image1DArray, + Sampler2DArray, + Image2DArray, + SamplerBuffer, + ImageBuffer, + Sampler2DMS, + Image2DMS, + Sampler2DMSArray, + Image2DMSArray, + SamplerCubeArray, + ImageCubeArray, + Sampler1DShadow, + Sampler2DShadow, + Sampler2DRectShadow, + Sampler1DArrayShadow, + Sampler2DArrayShadow, + SamplerCubeShadow, + SamplerCubeArrayShadow, + // signed integer opaque types + ISampler1D, + IImage1D, + ISampler2D, + IImage2D, + ISampler3D, + IImage3D, + ISamplerCube, + IImageCube, + ISampler2DRect, + IImage2DRect, + ISampler1DArray, + IImage1DArray, + ISampler2DArray, + IImage2DArray, + ISamplerBuffer, + IImageBuffer, + ISampler2DMS, + IImage2DMS, + ISampler2DMSArray, + IImage2DMSArray, + ISamplerCubeArray, + IImageCubeArray, + // unsigned integer opaque types + AtomicUInt, + USampler1D, + UImage1D, + USampler2D, + UImage2D, + USampler3D, + UImage3D, + USamplerCube, + UImageCube, + USampler2DRect, + UImage2DRect, + USampler1DArray, + UImage1DArray, + USampler2DArray, + UImage2DArray, + USamplerBuffer, + UImageBuffer, + USampler2DMS, + UImage2DMS, + USampler2DMSArray, + UImage2DMSArray, + USamplerCubeArray, + UImageCubeArray, + Struct(SymRef), +} + +impl TypeKind { + pub fn is_sampler(&self) -> bool { + use TypeKind::*; + match self { + Sampler1D + | Image1D + | Sampler2D + | Image2D + | Sampler3D + | Image3D + | SamplerCube + | ImageCube + | Sampler2DRect + | Image2DRect + | Sampler1DArray + | Image1DArray + | Sampler2DArray + | Image2DArray + | SamplerBuffer + | ImageBuffer + | Sampler2DMS + | Image2DMS + | Sampler2DMSArray + | Image2DMSArray + | SamplerCubeArray + | ImageCubeArray + | Sampler1DShadow + | Sampler2DShadow + | Sampler2DRectShadow + | Sampler1DArrayShadow + | Sampler2DArrayShadow + | SamplerCubeShadow + | SamplerCubeArrayShadow + | ISampler1D + | IImage1D + | ISampler2D + | IImage2D + | ISampler3D + | IImage3D + | ISamplerCube + | IImageCube + | ISampler2DRect + | IImage2DRect + | ISampler1DArray + | IImage1DArray + | ISampler2DArray + | IImage2DArray + | ISamplerBuffer + | IImageBuffer + | ISampler2DMS + | IImage2DMS + | ISampler2DMSArray + | IImage2DMSArray + | ISamplerCubeArray + | IImageCubeArray + | USampler1D + | UImage1D + | USampler2D + | UImage2D + | USampler3D + | UImage3D + | USamplerCube + | UImageCube + | USampler2DRect + | UImage2DRect + | USampler1DArray + | UImage1DArray + | USampler2DArray + | UImage2DArray + | USamplerBuffer + | UImageBuffer + | USampler2DMS + | UImage2DMS + | USampler2DMSArray + | UImage2DMSArray + | USamplerCubeArray + | UImageCubeArray => true, + _ => false, + } + } + + pub fn glsl_primitive_type_name(&self) -> Option<&'static str> { + use TypeKind::*; + Some(match self { + Void => "void", + Bool => "bool", + Int => "int", + UInt => "uint", + Float => "float", + Double => "double", + Vec2 => "vec2", + Vec3 => "vec3", + Vec4 => "vec4", + DVec2 => "dvec2", + DVec3 => "dvec3", + DVec4 => "dvec4", + BVec2 => "bvec2", + BVec3 => "bvec3", + BVec4 => "bvec4", + IVec2 => "ivec2", + IVec3 => "ivec3", + IVec4 => "ivec4", + UVec2 => "uvec2", + UVec3 => "uvec3", + UVec4 => "uvec4", + Mat2 => "mat2", + Mat3 => "mat3", + Mat4 => "mat4", + Mat23 => "mat23", + Mat24 => "mat24", + Mat32 => "mat32", + Mat34 => "mat34", + Mat42 => "mat42", + Mat43 => "mat43", + DMat2 => "dmat2", + DMat3 => "dmat3", + DMat4 => "dmat4", + DMat23 => "dmat23", + DMat24 => "dmat24", + DMat32 => "dmat32", + DMat34 => "dmat34", + DMat42 => "dmat42", + DMat43 => "dmat43", + Sampler1D => "sampler1D", + Image1D => "image1D", + Sampler2D => "sampler2D", + Image2D => "image2D", + Sampler3D => "sampler3D", + Image3D => "image3D", + SamplerCube => "samplerCube", + ImageCube => "imageCube", + Sampler2DRect => "sampler2DRect", + Image2DRect => "image2DRect", + Sampler1DArray => "sampler1DArray", + Image1DArray => "image1DArray", + Sampler2DArray => "sampler2DArray", + Image2DArray => "image2DArray", + SamplerBuffer => "samplerBuffer", + ImageBuffer => "imageBuffer", + Sampler2DMS => "sampler2DMS", + Image2DMS => "image2DMS", + Sampler2DMSArray => "sampler2DMSArray", + Image2DMSArray => "image2DMSArray", + SamplerCubeArray => "samplerCubeArray", + ImageCubeArray => "imageCubeArray", + Sampler1DShadow => "sampler1DShadow", + Sampler2DShadow => "sampler2DShadow", + Sampler2DRectShadow => "sampler2DRectShadow", + Sampler1DArrayShadow => "sampler1DArrayShadow", + Sampler2DArrayShadow => "sampler2DArrayShadow", + SamplerCubeShadow => "samplerCubeShadow", + SamplerCubeArrayShadow => "samplerCubeArrayShadow", + ISampler1D => "isampler1D", + IImage1D => "iimage1D", + ISampler2D => "isampler2D", + IImage2D => "iimage2D", + ISampler3D => "isampler3D", + IImage3D => "iimage3D", + ISamplerCube => "isamplerCube", + IImageCube => "iimageCube", + ISampler2DRect => "isampler2DRect", + IImage2DRect => "iimage2DRect", + ISampler1DArray => "isampler1DArray", + IImage1DArray => "iimage1DArray", + ISampler2DArray => "isampler2DArray", + IImage2DArray => "iimage2DArray", + ISamplerBuffer => "isamplerBuffer", + IImageBuffer => "iimageBuffer", + ISampler2DMS => "isampler2MS", + IImage2DMS => "iimage2DMS", + ISampler2DMSArray => "isampler2DMSArray", + IImage2DMSArray => "iimage2DMSArray", + ISamplerCubeArray => "isamplerCubeArray", + IImageCubeArray => "iimageCubeArray", + AtomicUInt => "atomic_uint", + USampler1D => "usampler1D", + UImage1D => "uimage1D", + USampler2D => "usampler2D", + UImage2D => "uimage2D", + USampler3D => "usampler3D", + UImage3D => "uimage3D", + USamplerCube => "usamplerCube", + UImageCube => "uimageCube", + USampler2DRect => "usampler2DRect", + UImage2DRect => "uimage2DRect", + USampler1DArray => "usampler1DArray", + UImage1DArray => "uimage1DArray", + USampler2DArray => "usampler2DArray", + UImage2DArray => "uimage2DArray", + USamplerBuffer => "usamplerBuffer", + UImageBuffer => "uimageBuffer", + USampler2DMS => "usampler2DMS", + UImage2DMS => "uimage2DMS", + USampler2DMSArray => "usamplerDMSArray", + UImage2DMSArray => "uimage2DMSArray", + USamplerCubeArray => "usamplerCubeArray", + UImageCubeArray => "uimageCubeArray", + Struct(..) => return None, + }) + } + + pub fn cxx_primitive_type_name(&self) -> Option<&'static str> { + use TypeKind::*; + match self { + Bool => Some("Bool"), + Int => Some("I32"), + UInt => Some("U32"), + Float => Some("Float"), + Double => Some("Double"), + _ => self.glsl_primitive_type_name(), + } + } + + pub fn cxx_primitive_scalar_type_name(&self) -> Option<&'static str> { + use TypeKind::*; + match self { + Void => Some("void"), + Bool => Some("bool"), + Int => Some("int32_t"), + UInt => Some("uint32_t"), + Float => Some("float"), + Double => Some("double"), + _ => { + if self.is_sampler() { + self.cxx_primitive_type_name() + } else { + None + } + } + } + } + + pub fn from_glsl_primitive_type_name(name: &str) -> Option { + use TypeKind::*; + Some(match name { + "void" => Void, + "bool" => Bool, + "int" => Int, + "uint" => UInt, + "float" => Float, + "double" => Double, + "vec2" => Vec2, + "vec3" => Vec3, + "vec4" => Vec4, + "dvec2" => DVec2, + "dvec3" => DVec3, + "dvec4" => DVec4, + "bvec2" => BVec2, + "bvec3" => BVec3, + "bvec4" => BVec4, + "ivec2" => IVec2, + "ivec3" => IVec3, + "ivec4" => IVec4, + "uvec2" => UVec2, + "uvec3" => UVec3, + "uvec4" => UVec4, + "mat2" => Mat2, + "mat3" => Mat3, + "mat4" => Mat4, + "mat23" => Mat23, + "mat24" => Mat24, + "mat32" => Mat32, + "mat34" => Mat34, + "mat42" => Mat42, + "mat43" => Mat43, + "dmat2" => DMat2, + "dmat3" => DMat3, + "dmat4" => DMat4, + "dmat23" => DMat23, + "dmat24" => DMat24, + "dmat32" => DMat32, + "dmat34" => DMat34, + "dmat42" => DMat42, + "dmat43" => DMat43, + "sampler1D" => Sampler1D, + "image1D" => Image1D, + "sampler2D" => Sampler2D, + "image2D" => Image2D, + "sampler3D" => Sampler3D, + "image3D" => Image3D, + "samplerCube" => SamplerCube, + "imageCube" => ImageCube, + "sampler2DRect" => Sampler2DRect, + "image2DRect" => Image2DRect, + "sampler1DArray" => Sampler1DArray, + "image1DArray" => Image1DArray, + "sampler2DArray" => Sampler2DArray, + "image2DArray" => Image2DArray, + "samplerBuffer" => SamplerBuffer, + "imageBuffer" => ImageBuffer, + "sampler2DMS" => Sampler2DMS, + "image2DMS" => Image2DMS, + "sampler2DMSArray" => Sampler2DMSArray, + "image2DMSArray" => Image2DMSArray, + "samplerCubeArray" => SamplerCubeArray, + "imageCubeArray" => ImageCubeArray, + "sampler1DShadow" => Sampler1DShadow, + "sampler2DShadow" => Sampler2DShadow, + "sampler2DRectShadow" => Sampler2DRectShadow, + "sampler1DArrayShadow" => Sampler1DArrayShadow, + "sampler2DArrayShadow" => Sampler2DArrayShadow, + "samplerCubeShadow" => SamplerCubeShadow, + "samplerCubeArrayShadow" => SamplerCubeArrayShadow, + "isampler1D" => ISampler1D, + "iimage1D" => IImage1D, + "isampler2D" => ISampler2D, + "iimage2D" => IImage2D, + "isampler3D" => ISampler3D, + "iimage3D" => IImage3D, + "isamplerCube" => ISamplerCube, + "iimageCube" => IImageCube, + "isampler2DRect" => ISampler2DRect, + "iimage2DRect" => IImage2DRect, + "isampler1DArray" => ISampler1DArray, + "iimage1DArray" => IImage1DArray, + "isampler2DArray" => ISampler2DArray, + "iimage2DArray" => IImage2DArray, + "isamplerBuffer" => ISamplerBuffer, + "iimageBuffer" => IImageBuffer, + "isampler2MS" => ISampler2DMS, + "iimage2DMS" => IImage2DMS, + "isampler2DMSArray" => ISampler2DMSArray, + "iimage2DMSArray" => IImage2DMSArray, + "isamplerCubeArray" => ISamplerCubeArray, + "iimageCubeArray" => IImageCubeArray, + "atomic_uint" => AtomicUInt, + "usampler1D" => USampler1D, + "uimage1D" => UImage1D, + "usampler2D" => USampler2D, + "uimage2D" => UImage2D, + "usampler3D" => USampler3D, + "uimage3D" => UImage3D, + "usamplerCube" => USamplerCube, + "uimageCube" => UImageCube, + "usampler2DRect" => USampler2DRect, + "uimage2DRect" => UImage2DRect, + "usampler1DArray" => USampler1DArray, + "uimage1DArray" => UImage1DArray, + "usampler2DArray" => USampler2DArray, + "uimage2DArray" => UImage2DArray, + "usamplerBuffer" => USamplerBuffer, + "uimageBuffer" => UImageBuffer, + "usampler2DMS" => USampler2DMS, + "uimage2DMS" => UImage2DMS, + "usamplerDMSArray" => USampler2DMSArray, + "uimage2DMSArray" => UImage2DMSArray, + "usamplerCubeArray" => USamplerCubeArray, + "uimageCubeArray" => UImageCubeArray, + _ => return None, + }) + } + + pub fn from_primitive_type_specifier(spec: &syntax::TypeSpecifierNonArray) -> Option { + use TypeKind::*; + Some(match spec { + TypeSpecifierNonArray::Void => Void, + TypeSpecifierNonArray::Bool => Bool, + TypeSpecifierNonArray::Int => Int, + TypeSpecifierNonArray::UInt => UInt, + TypeSpecifierNonArray::Float => Float, + TypeSpecifierNonArray::Double => Double, + TypeSpecifierNonArray::Vec2 => Vec2, + TypeSpecifierNonArray::Vec3 => Vec3, + TypeSpecifierNonArray::Vec4 => Vec4, + TypeSpecifierNonArray::DVec2 => DVec2, + TypeSpecifierNonArray::DVec3 => DVec3, + TypeSpecifierNonArray::DVec4 => DVec4, + TypeSpecifierNonArray::BVec2 => BVec2, + TypeSpecifierNonArray::BVec3 => BVec3, + TypeSpecifierNonArray::BVec4 => BVec4, + TypeSpecifierNonArray::IVec2 => IVec2, + TypeSpecifierNonArray::IVec3 => IVec3, + TypeSpecifierNonArray::IVec4 => IVec4, + TypeSpecifierNonArray::UVec2 => UVec2, + TypeSpecifierNonArray::UVec3 => UVec3, + TypeSpecifierNonArray::UVec4 => UVec4, + TypeSpecifierNonArray::Mat2 => Mat2, + TypeSpecifierNonArray::Mat3 => Mat3, + TypeSpecifierNonArray::Mat4 => Mat4, + TypeSpecifierNonArray::Mat23 => Mat23, + TypeSpecifierNonArray::Mat24 => Mat24, + TypeSpecifierNonArray::Mat32 => Mat32, + TypeSpecifierNonArray::Mat34 => Mat34, + TypeSpecifierNonArray::Mat42 => Mat42, + TypeSpecifierNonArray::Mat43 => Mat43, + TypeSpecifierNonArray::DMat2 => DMat2, + TypeSpecifierNonArray::DMat3 => DMat3, + TypeSpecifierNonArray::DMat4 => DMat4, + TypeSpecifierNonArray::DMat23 => DMat23, + TypeSpecifierNonArray::DMat24 => DMat24, + TypeSpecifierNonArray::DMat32 => DMat32, + TypeSpecifierNonArray::DMat34 => DMat34, + TypeSpecifierNonArray::DMat42 => DMat42, + TypeSpecifierNonArray::DMat43 => DMat43, + TypeSpecifierNonArray::Sampler1D => Sampler1D, + TypeSpecifierNonArray::Image1D => Image1D, + TypeSpecifierNonArray::Sampler2D => Sampler2D, + TypeSpecifierNonArray::Image2D => Image2D, + TypeSpecifierNonArray::Sampler3D => Sampler3D, + TypeSpecifierNonArray::Image3D => Image3D, + TypeSpecifierNonArray::SamplerCube => SamplerCube, + TypeSpecifierNonArray::ImageCube => ImageCube, + TypeSpecifierNonArray::Sampler2DRect => Sampler2DRect, + TypeSpecifierNonArray::Image2DRect => Image2DRect, + TypeSpecifierNonArray::Sampler1DArray => Sampler1DArray, + TypeSpecifierNonArray::Image1DArray => Image1DArray, + TypeSpecifierNonArray::Sampler2DArray => Sampler2DArray, + TypeSpecifierNonArray::Image2DArray => Image2DArray, + TypeSpecifierNonArray::SamplerBuffer => SamplerBuffer, + TypeSpecifierNonArray::ImageBuffer => ImageBuffer, + TypeSpecifierNonArray::Sampler2DMS => Sampler2DMS, + TypeSpecifierNonArray::Image2DMS => Image2DMS, + TypeSpecifierNonArray::Sampler2DMSArray => Sampler2DMSArray, + TypeSpecifierNonArray::Image2DMSArray => Image2DMSArray, + TypeSpecifierNonArray::SamplerCubeArray => SamplerCubeArray, + TypeSpecifierNonArray::ImageCubeArray => ImageCubeArray, + TypeSpecifierNonArray::Sampler1DShadow => Sampler1DShadow, + TypeSpecifierNonArray::Sampler2DShadow => Sampler2DShadow, + TypeSpecifierNonArray::Sampler2DRectShadow => Sampler2DRectShadow, + TypeSpecifierNonArray::Sampler1DArrayShadow => Sampler1DArrayShadow, + TypeSpecifierNonArray::Sampler2DArrayShadow => Sampler2DArrayShadow, + TypeSpecifierNonArray::SamplerCubeShadow => SamplerCubeShadow, + TypeSpecifierNonArray::SamplerCubeArrayShadow => SamplerCubeArrayShadow, + TypeSpecifierNonArray::ISampler1D => ISampler1D, + TypeSpecifierNonArray::IImage1D => IImage1D, + TypeSpecifierNonArray::ISampler2D => ISampler2D, + TypeSpecifierNonArray::IImage2D => IImage2D, + TypeSpecifierNonArray::ISampler3D => ISampler3D, + TypeSpecifierNonArray::IImage3D => IImage3D, + TypeSpecifierNonArray::ISamplerCube => ISamplerCube, + TypeSpecifierNonArray::IImageCube => IImageCube, + TypeSpecifierNonArray::ISampler2DRect => ISampler2DRect, + TypeSpecifierNonArray::IImage2DRect => IImage2DRect, + TypeSpecifierNonArray::ISampler1DArray => ISampler1DArray, + TypeSpecifierNonArray::IImage1DArray => IImage1DArray, + TypeSpecifierNonArray::ISampler2DArray => ISampler2DArray, + TypeSpecifierNonArray::IImage2DArray => IImage2DArray, + TypeSpecifierNonArray::ISamplerBuffer => ISamplerBuffer, + TypeSpecifierNonArray::IImageBuffer => IImageBuffer, + TypeSpecifierNonArray::ISampler2DMS => ISampler2DMS, + TypeSpecifierNonArray::IImage2DMS => IImage2DMS, + TypeSpecifierNonArray::ISampler2DMSArray => ISampler2DMSArray, + TypeSpecifierNonArray::IImage2DMSArray => IImage2DMSArray, + TypeSpecifierNonArray::ISamplerCubeArray => ISamplerCubeArray, + TypeSpecifierNonArray::IImageCubeArray => IImageCubeArray, + TypeSpecifierNonArray::AtomicUInt => AtomicUInt, + TypeSpecifierNonArray::USampler1D => USampler1D, + TypeSpecifierNonArray::UImage1D => UImage1D, + TypeSpecifierNonArray::USampler2D => USampler2D, + TypeSpecifierNonArray::UImage2D => UImage2D, + TypeSpecifierNonArray::USampler3D => USampler3D, + TypeSpecifierNonArray::UImage3D => UImage3D, + TypeSpecifierNonArray::USamplerCube => USamplerCube, + TypeSpecifierNonArray::UImageCube => UImageCube, + TypeSpecifierNonArray::USampler2DRect => USampler2DRect, + TypeSpecifierNonArray::UImage2DRect => UImage2DRect, + TypeSpecifierNonArray::USampler1DArray => USampler1DArray, + TypeSpecifierNonArray::UImage1DArray => UImage1DArray, + TypeSpecifierNonArray::USampler2DArray => USampler2DArray, + TypeSpecifierNonArray::UImage2DArray => UImage2DArray, + TypeSpecifierNonArray::USamplerBuffer => USamplerBuffer, + TypeSpecifierNonArray::UImageBuffer => UImageBuffer, + TypeSpecifierNonArray::USampler2DMS => USampler2DMS, + TypeSpecifierNonArray::UImage2DMS => UImage2DMS, + TypeSpecifierNonArray::USampler2DMSArray => USampler2DMSArray, + TypeSpecifierNonArray::UImage2DMSArray => UImage2DMSArray, + TypeSpecifierNonArray::USamplerCubeArray => USamplerCubeArray, + TypeSpecifierNonArray::UImageCubeArray => UImageCubeArray, + TypeSpecifierNonArray::Struct(..) | TypeSpecifierNonArray::TypeName(..) => return None, + }) + } +} + +impl LiftFrom<&syntax::TypeSpecifierNonArray> for TypeKind { + fn lift(state: &mut State, spec: &syntax::TypeSpecifierNonArray) -> Self { + use TypeKind::*; + if let Some(kind) = TypeKind::from_primitive_type_specifier(spec) { + kind + } else { + match spec { + TypeSpecifierNonArray::Struct(s) => { + Struct(state.lookup(s.name.as_ref().unwrap().as_str()).unwrap()) + } + TypeSpecifierNonArray::TypeName(s) => Struct(state.lookup(&s.0).unwrap()), + _ => unreachable!(), + } + } + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct Type { + pub kind: TypeKind, + pub precision: Option, + pub array_sizes: Option>, +} + +impl Type { + pub fn new(kind: TypeKind) -> Self { + Type { + kind, + precision: None, + array_sizes: None, + } + } +} + +impl LiftFrom<&syntax::FullySpecifiedType> for Type { + fn lift(state: &mut State, ty: &syntax::FullySpecifiedType) -> Self { + let kind = lift(state, &ty.ty.ty); + let array_sizes = match ty.ty.array_specifier.as_ref() { + Some(x) => Some(Box::new(lift(state, x))), + None => None, + }; + let precision = get_precision(&ty.qualifier); + Type { + kind, + precision, + array_sizes, + } + } +} + +impl LiftFrom<&syntax::TypeSpecifier> for Type { + fn lift(state: &mut State, ty: &syntax::TypeSpecifier) -> Self { + let kind = lift(state, &ty.ty); + let array_sizes = ty + .array_specifier + .as_ref() + .map(|x| Box::new(lift(state, x))); + Type { + kind, + precision: None, + array_sizes, + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct StructField { + pub ty: Type, + pub name: syntax::Identifier, +} + +fn get_precision(qualifiers: &Option) -> Option { + let mut precision = None; + for qual in qualifiers.iter().flat_map(|x| x.qualifiers.0.iter()) { + match qual { + syntax::TypeQualifierSpec::Precision(p) => { + if precision.is_some() { + panic!("Multiple precisions"); + } + precision = Some(p.clone()); + } + _ => {} + } + } + precision +} + +impl LiftFrom<&StructFieldSpecifier> for StructField { + fn lift(state: &mut State, f: &StructFieldSpecifier) -> Self { + let mut ty: Type = lift(state, &f.ty); + match &f.identifiers.0[..] { + [ident] => { + if let Some(a) = &ident.array_spec { + ty.array_sizes = Some(Box::new(lift(state, a))); + } + StructField { + ty, + name: ident.ident.clone(), + } + } + _ => panic!("bad number of identifiers"), + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct StructFields { + pub fields: Vec, +} + +impl LiftFrom<&StructSpecifier> for StructFields { + fn lift(state: &mut State, s: &StructSpecifier) -> Self { + let fields = s.fields.0.iter().map(|field| lift(state, field)).collect(); + Self { fields } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum RunClass { + Unknown, + Scalar, + Vector, + Dependent(u32), +} + +impl RunClass { + pub fn merge(self, run_class: RunClass) -> RunClass { + match (self, run_class) { + (RunClass::Vector, _) | (_, RunClass::Vector) => RunClass::Vector, + (RunClass::Dependent(x), RunClass::Dependent(y)) => RunClass::Dependent(x | y), + (RunClass::Unknown, _) | (_, RunClass::Dependent(..)) => run_class, + _ => self, + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum SymDecl { + NativeFunction(FunctionType, Option<&'static str>), + UserFunction(Rc, RunClass), + Local(StorageClass, Type, RunClass), + Global( + StorageClass, + Option, + Type, + RunClass, + ), + Struct(StructFields), +} + +#[derive(Clone, Debug, PartialEq, Copy, Eq, Hash)] +pub struct SymRef(u32); + +#[derive(Debug)] +struct Scope { + name: String, + names: HashMap, +} +impl Scope { + fn new(name: String) -> Self { + Scope { + name, + names: HashMap::new(), + } + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct TexelFetchOffsets { + pub min_x: i32, + pub max_x: i32, + pub min_y: i32, + pub max_y: i32, +} + +impl TexelFetchOffsets { + fn new(x: i32, y: i32) -> Self { + TexelFetchOffsets { + min_x: x, + max_x: x, + min_y: y, + max_y: y, + } + } + + fn add_offset(&mut self, x: i32, y: i32) { + self.min_x = self.min_x.min(x); + self.max_x = self.max_x.max(x); + self.min_y = self.min_y.min(y); + self.max_y = self.max_y.max(y); + } +} + +#[derive(Debug)] +pub struct State { + scopes: Vec, + syms: Vec>, + in_function: Option, + run_class_changed: Cell, + last_declaration: SymRef, + branch_run_class: RunClass, + branch_declaration: SymRef, + modified_globals: RefCell>, + pub used_fragcoord: i32, + pub used_globals: RefCell>, + pub texel_fetches: HashMap<(SymRef, SymRef), TexelFetchOffsets>, +} + +impl State { + pub fn new() -> Self { + State { + scopes: Vec::new(), + syms: Vec::new(), + in_function: None, + run_class_changed: Cell::new(false), + last_declaration: SymRef(0), + branch_run_class: RunClass::Unknown, + branch_declaration: SymRef(0), + modified_globals: RefCell::new(Vec::new()), + used_fragcoord: 0, + used_globals: RefCell::new(Vec::new()), + texel_fetches: HashMap::new(), + } + } + + pub fn lookup(&self, name: &str) -> Option { + for s in self.scopes.iter().rev() { + if let Some(sym) = s.names.get(name) { + return Some(*sym); + } + } + return None; + } + + fn declare(&mut self, name: &str, decl: SymDecl) -> SymRef { + let s = SymRef(self.syms.len() as u32); + self.syms.push(RefCell::new(Symbol { + name: name.into(), + decl, + })); + self.scopes.last_mut().unwrap().names.insert(name.into(), s); + s + } + + pub fn sym(&self, sym: SymRef) -> Ref { + self.syms[sym.0 as usize].borrow() + } + + pub fn sym_mut(&mut self, sym: SymRef) -> &mut Symbol { + self.syms[sym.0 as usize].get_mut() + } + + pub fn lookup_sym_mut(&mut self, name: &str) -> Option<&mut Symbol> { + self.lookup(name) + .map(move |x| self.syms[x.0 as usize].get_mut()) + } + + fn push_scope(&mut self, name: String) { + self.scopes.push(Scope::new(name)); + } + fn pop_scope(&mut self) { + self.scopes.pop(); + } + + fn return_run_class(&self, mut new_run_class: RunClass) { + new_run_class = self.branch_run_class.merge(new_run_class); + if let Some(sym) = self.in_function { + let mut b = self.syms[sym.0 as usize].borrow_mut(); + if let SymDecl::UserFunction(_, ref mut run_class) = b.decl { + *run_class = run_class.merge(new_run_class); + } + } + } + + pub fn function_definition(&self, name: SymRef) -> Option<(Rc, RunClass)> { + if let SymDecl::UserFunction(ref fd, ref run_class) = &self.sym(name).decl { + Some((fd.clone(), *run_class)) + } else { + None + } + } + + fn merge_run_class(&self, sym: SymRef, mut new_run_class: RunClass) -> RunClass { + if sym.0 <= self.branch_declaration.0 { + new_run_class = self.branch_run_class.merge(new_run_class); + } + let mut b = self.syms[sym.0 as usize].borrow_mut(); + let mut old_run_class = new_run_class; + if let SymDecl::Local(_, _, ref mut run_class) = b.decl { + old_run_class = *run_class; + new_run_class = old_run_class.merge(new_run_class); + *run_class = new_run_class; + } + if old_run_class != RunClass::Unknown && old_run_class != new_run_class { + self.run_class_changed.set(true); + } + new_run_class + } +} + +/// A declaration. +#[derive(Clone, Debug, PartialEq)] +pub enum Declaration { + FunctionPrototype(FunctionPrototype), + StructDefinition(SymRef), + InitDeclaratorList(InitDeclaratorList), + Precision(PrecisionQualifier, TypeSpecifier), + Block(Block), + Global(TypeQualifier, Vec), +} + +/// A general purpose block, containing fields and possibly a list of declared identifiers. Semantic +/// is given with the storage qualifier. +#[derive(Clone, Debug, PartialEq)] +pub struct Block { + pub qualifier: TypeQualifier, + pub name: Identifier, + pub fields: Vec, + pub identifier: Option, +} + +/// Function identifier. +#[derive(Clone, Debug, PartialEq)] +pub enum FunIdentifier { + Identifier(SymRef), + Constructor(Type), +} + +/// Function prototype. +#[derive(Clone, Debug, PartialEq)] +pub struct FunctionPrototype { + pub ty: Type, + pub name: Identifier, + pub parameters: Vec, +} + +impl FunctionPrototype { + pub fn has_parameter(&self, sym: SymRef) -> bool { + for param in &self.parameters { + match param { + FunctionParameterDeclaration::Named(_, ref d) => { + if d.sym == sym { + return true; + } + } + _ => {} + } + } + false + } +} + +/// Function parameter declaration. +#[derive(Clone, Debug, PartialEq)] +pub enum FunctionParameterDeclaration { + Named(Option, FunctionParameterDeclarator), + Unnamed(Option, TypeSpecifier), +} + +/// Function parameter declarator. +#[derive(Clone, Debug, PartialEq)] +pub struct FunctionParameterDeclarator { + pub ty: Type, + pub name: Identifier, + pub sym: SymRef, +} + +/// Init declarator list. +#[derive(Clone, Debug, PartialEq)] +pub struct InitDeclaratorList { + // XXX it feels like separating out the type and the names is better than + // head and tail + // Also, it might be nice to separate out type definitions from name definitions + pub head: SingleDeclaration, + pub tail: Vec, +} + +/// Type qualifier. +#[derive(Clone, Debug, PartialEq)] +pub struct TypeQualifier { + pub qualifiers: NonEmpty, +} + +fn lift_type_qualifier_for_declaration( + _state: &mut State, + q: &Option, +) -> Option { + q.as_ref().and_then(|x| { + NonEmpty::from_non_empty_iter(x.qualifiers.0.iter().flat_map(|x| match x { + syntax::TypeQualifierSpec::Precision(_) => None, + syntax::TypeQualifierSpec::Interpolation(_) => None, + syntax::TypeQualifierSpec::Invariant => Some(TypeQualifierSpec::Invariant), + syntax::TypeQualifierSpec::Layout(l) => Some(TypeQualifierSpec::Layout(l.clone())), + syntax::TypeQualifierSpec::Precise => Some(TypeQualifierSpec::Precise), + syntax::TypeQualifierSpec::Storage(_) => None, + })) + .map(|x| TypeQualifier { qualifiers: x }) + }) +} + +fn lift_type_qualifier_for_parameter( + _state: &mut State, + q: &Option, +) -> Option { + let mut qp: Option = None; + if let Some(q) = q { + for x in &q.qualifiers.0 { + match (&qp, x) { + (None, syntax::TypeQualifierSpec::Storage(s)) => match s { + syntax::StorageQualifier::Const => qp = Some(ParameterQualifier::Const), + syntax::StorageQualifier::In => qp = Some(ParameterQualifier::In), + syntax::StorageQualifier::Out => qp = Some(ParameterQualifier::Out), + syntax::StorageQualifier::InOut => qp = Some(ParameterQualifier::InOut), + _ => panic!("Bad storage qualifier for parameter"), + }, + (_, syntax::TypeQualifierSpec::Precision(_)) => {} + _ => panic!("Bad parameter qualifier {:?}", x), + } + } + } + qp +} + +#[derive(Clone, Debug, PartialEq)] +pub enum ParameterQualifier { + Const, + In, + InOut, + Out, +} + +#[derive(Clone, Debug, PartialEq)] +pub enum MemoryQualifier { + Coherent, + Volatile, + Restrict, + ReadOnly, + WriteOnly, +} + +/// Type qualifier spec. +#[derive(Clone, Debug, PartialEq)] +pub enum TypeQualifierSpec { + Layout(syntax::LayoutQualifier), + Invariant, + Parameter(ParameterQualifier), + Memory(MemoryQualifier), + Precise, +} + +/// Single declaration. +#[derive(Clone, Debug, PartialEq)] +pub struct SingleDeclaration { + pub ty: Type, + pub ty_def: Option, + pub qualifier: Option, + pub name: SymRef, + pub initializer: Option, +} + +/// A single declaration with implicit, already-defined type. +#[derive(Clone, Debug, PartialEq)] +pub struct SingleDeclarationNoType { + pub ident: ArrayedIdentifier, + pub initializer: Option, +} + +/// Initializer. +#[derive(Clone, Debug, PartialEq)] +pub enum Initializer { + Simple(Box), + List(NonEmpty), +} + +impl From for Initializer { + fn from(e: Expr) -> Self { + Initializer::Simple(Box::new(e)) + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct Expr { + pub kind: ExprKind, + pub ty: Type, +} + +#[derive(Clone, Debug, PartialEq)] +pub enum FieldSet { + Rgba, + Xyzw, + Stpq, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct SwizzleSelector { + pub field_set: FieldSet, + pub components: Vec, +} + +impl SwizzleSelector { + fn parse(s: &str) -> Self { + let mut components = Vec::new(); + let mut field_set = Vec::new(); + + for c in s.chars() { + match c { + 'r' => { + components.push(0); + field_set.push(FieldSet::Rgba); + } + 'x' => { + components.push(0); + field_set.push(FieldSet::Xyzw); + } + 's' => { + components.push(0); + field_set.push(FieldSet::Stpq); + } + + 'g' => { + components.push(1); + field_set.push(FieldSet::Rgba); + } + 'y' => { + components.push(1); + field_set.push(FieldSet::Xyzw); + } + 't' => { + components.push(1); + field_set.push(FieldSet::Stpq); + } + + 'b' => { + components.push(2); + field_set.push(FieldSet::Rgba); + } + 'z' => { + components.push(2); + field_set.push(FieldSet::Xyzw); + } + 'p' => { + components.push(2); + field_set.push(FieldSet::Stpq); + } + + 'a' => { + components.push(3); + field_set.push(FieldSet::Rgba); + } + 'w' => { + components.push(3); + field_set.push(FieldSet::Xyzw); + } + 'q' => { + components.push(3); + field_set.push(FieldSet::Stpq); + } + _ => panic!("bad selector"), + } + } + + let first = &field_set[0]; + assert!(field_set.iter().all(|item| item == first)); + assert!(components.len() <= 4); + SwizzleSelector { + field_set: first.clone(), + components, + } + } + + pub fn to_string(&self) -> String { + let mut s = String::new(); + let fs = match self.field_set { + FieldSet::Rgba => ['r', 'g', 'b', 'a'], + FieldSet::Xyzw => ['x', 'y', 'z', 'w'], + FieldSet::Stpq => ['s', 't', 'p', 'q'], + }; + for i in &self.components { + s.push(fs[*i as usize]) + } + s + } +} + +/// The most general form of an expression. As you can see if you read the variant list, in GLSL, an +/// assignment is an expression. This is a bit silly but think of an assignment as a statement first +/// then an expression which evaluates to what the statement “returns”. +/// +/// An expression is either an assignment or a list (comma) of assignments. +#[derive(Clone, Debug, PartialEq)] +pub enum ExprKind { + /// A variable expression, using an identifier. + Variable(SymRef), + /// Integral constant expression. + IntConst(i32), + /// Unsigned integral constant expression. + UIntConst(u32), + /// Boolean constant expression. + BoolConst(bool), + /// Single precision floating expression. + FloatConst(f32), + /// Double precision floating expression. + DoubleConst(f64), + /// A unary expression, gathering a single expression and a unary operator. + Unary(UnaryOp, Box), + /// A binary expression, gathering two expressions and a binary operator. + Binary(BinaryOp, Box, Box), + /// A ternary conditional expression, gathering three expressions. + Ternary(Box, Box, Box), + /// An assignment is also an expression. Gathers an expression that defines what to assign to, an + /// assignment operator and the value to associate with. + Assignment(Box, AssignmentOp, Box), + /// Add an array specifier to an expression. + Bracket(Box, Box), + /// A functional call. It has a function identifier and a list of expressions (arguments). + FunCall(FunIdentifier, Vec), + /// An expression associated with a field selection (struct). + Dot(Box, Identifier), + /// An expression associated with a component selection + SwizzleSelector(Box, SwizzleSelector), + /// Post-incrementation of an expression. + PostInc(Box), + /// Post-decrementation of an expression. + PostDec(Box), + /// An expression that contains several, separated with comma. + Comma(Box, Box), + /// A temporary condition variable + Cond(usize, Box), + CondMask, +} + +/* +impl From for Expr { + fn from(x: i32) -> Expr { + ExprKind::IntConst(x) + } +} + +impl From for Expr { + fn from(x: u32) -> Expr { + Expr::UIntConst(x) + } +} + +impl From for Expr { + fn from(x: bool) -> Expr { + Expr::BoolConst(x) + } +} + +impl From for Expr { + fn from(x: f32) -> Expr { + Expr::FloatConst(x) + } +} + +impl From for Expr { + fn from(x: f64) -> Expr { + Expr::DoubleConst(x) + } +} +*/ +/// Starting rule. +#[derive(Clone, Debug, PartialEq)] +pub struct TranslationUnit(pub NonEmpty); + +impl TranslationUnit { + /// Construct a translation unit from an iterator. + /// + /// # Errors + /// + /// `None` if the iterator yields no value. + pub fn from_iter(iter: I) -> Option + where + I: IntoIterator, + { + NonEmpty::from_non_empty_iter(iter).map(TranslationUnit) + } +} + +impl Deref for TranslationUnit { + type Target = NonEmpty; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for TranslationUnit { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl IntoIterator for TranslationUnit { + type IntoIter = as IntoIterator>::IntoIter; + type Item = ExternalDeclaration; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl<'a> IntoIterator for &'a TranslationUnit { + type IntoIter = <&'a NonEmpty as IntoIterator>::IntoIter; + type Item = &'a ExternalDeclaration; + + fn into_iter(self) -> Self::IntoIter { + (&self.0).into_iter() + } +} + +impl<'a> IntoIterator for &'a mut TranslationUnit { + type IntoIter = <&'a mut NonEmpty as IntoIterator>::IntoIter; + type Item = &'a mut ExternalDeclaration; + + fn into_iter(self) -> Self::IntoIter { + (&mut self.0).into_iter() + } +} + +/// External declaration. +#[derive(Clone, Debug, PartialEq)] +pub enum ExternalDeclaration { + Preprocessor(syntax::Preprocessor), + FunctionDefinition(Rc), + Declaration(Declaration), +} + +/// Function definition. +#[derive(Clone, Debug, PartialEq)] +pub struct FunctionDefinition { + pub prototype: FunctionPrototype, + pub body: CompoundStatement, + pub globals: Vec, + pub texel_fetches: HashMap<(SymRef, SymRef), TexelFetchOffsets>, +} + +/// Compound statement (with no new scope). +#[derive(Clone, Debug, PartialEq)] +pub struct CompoundStatement { + pub statement_list: Vec, +} + +impl CompoundStatement { + pub fn new() -> Self { + CompoundStatement { + statement_list: Vec::new(), + } + } +} + +impl FromIterator for CompoundStatement { + fn from_iter(iter: T) -> Self + where + T: IntoIterator, + { + CompoundStatement { + statement_list: iter.into_iter().collect(), + } + } +} + +/// Statement. +#[derive(Clone, Debug, PartialEq)] +pub enum Statement { + Compound(Box), + Simple(Box), +} + +/// Simple statement. +#[derive(Clone, Debug, PartialEq)] +pub enum SimpleStatement { + Declaration(Declaration), + Expression(ExprStatement), + Selection(SelectionStatement), + Switch(SwitchStatement), + Iteration(IterationStatement), + Jump(JumpStatement), +} + +impl SimpleStatement { + /// Create a new expression statement. + pub fn new_expr(expr: E) -> Self + where + E: Into, + { + SimpleStatement::Expression(Some(expr.into())) + } + + /// Create a new selection statement (if / else). + pub fn new_if_else(ife: If, truee: True, falsee: False) -> Self + where + If: Into, + True: Into, + False: Into, + { + SimpleStatement::Selection(SelectionStatement { + cond: Box::new(ife.into()), + body: Box::new(truee.into()), + else_stmt: Some(Box::new(falsee.into())), + }) + } + + /// Create a new while statement. + pub fn new_while(cond: C, body: S) -> Self + where + C: Into, + S: Into, + { + SimpleStatement::Iteration(IterationStatement::While( + cond.into(), + Box::new(body.into()), + )) + } + + /// Create a new do-while statement. + pub fn new_do_while(body: S, cond: C) -> Self + where + S: Into, + C: Into, + { + SimpleStatement::Iteration(IterationStatement::DoWhile( + Box::new(body.into()), + Box::new(cond.into()), + )) + } +} + +/// Expression statement. +pub type ExprStatement = Option; + +/// Selection statement. +#[derive(Clone, Debug, PartialEq)] +pub struct SelectionStatement { + pub cond: Box, + pub body: Box, + // the else branch + pub else_stmt: Option>, +} + +/// Condition. +#[derive(Clone, Debug, PartialEq)] +pub enum Condition { + Expr(Box), +} + +impl From for Condition { + fn from(expr: Expr) -> Self { + Condition::Expr(Box::new(expr)) + } +} + +/// Switch statement. +#[derive(Clone, Debug, PartialEq)] +pub struct SwitchStatement { + pub head: Box, + pub cases: Vec, +} + +/// Case label statement. +#[derive(Clone, Debug, PartialEq)] +pub enum CaseLabel { + Case(Box), + Def, +} + +/// An individual case +#[derive(Clone, Debug, PartialEq)] +pub struct Case { + pub label: CaseLabel, + pub stmts: Vec, +} + +/// Iteration statement. +#[derive(Clone, Debug, PartialEq)] +pub enum IterationStatement { + While(Condition, Box), + DoWhile(Box, Box), + For(ForInitStatement, ForRestStatement, Box), +} + +/// For init statement. +#[derive(Clone, Debug, PartialEq)] +pub enum ForInitStatement { + Expression(Option), + Declaration(Box), +} + +/// For init statement. +#[derive(Clone, Debug, PartialEq)] +pub struct ForRestStatement { + pub condition: Option, + pub post_expr: Option>, +} + +/// Jump statement. +#[derive(Clone, Debug, PartialEq)] +pub enum JumpStatement { + Continue, + Break, + Return(Option>), + Discard, +} + +trait NonEmptyExt { + fn map U>(&self, s: &mut State, f: F) -> NonEmpty; + fn new(x: T) -> NonEmpty; +} + +impl NonEmptyExt for NonEmpty { + fn map U>(&self, s: &mut State, mut f: F) -> NonEmpty { + NonEmpty::from_non_empty_iter(self.into_iter().map(|x| f(s, &x))).unwrap() + } + fn new(x: T) -> NonEmpty { + NonEmpty::from_non_empty_iter(vec![x].into_iter()).unwrap() + } +} + +fn translate_initializater(state: &mut State, i: &syntax::Initializer) -> Initializer { + match i { + syntax::Initializer::Simple(i) => { + Initializer::Simple(Box::new(translate_expression(state, i))) + } + _ => panic!(), + } +} + +fn translate_struct_declaration(state: &mut State, d: &syntax::SingleDeclaration) -> Declaration { + let ty = d.ty.clone(); + let ty_def = match &ty.ty.ty { + TypeSpecifierNonArray::Struct(s) => { + let decl = SymDecl::Struct(lift(state, s)); + Some(state.declare(s.name.as_ref().unwrap().as_str(), decl)) + } + _ => None, + }; + + let ty_def = ty_def.expect("Must be type definition"); + + Declaration::StructDefinition(ty_def) +} + +fn get_expr_index(e: &syntax::Expr) -> i32 { + match e { + syntax::Expr::IntConst(i) => *i, + syntax::Expr::UIntConst(u) => *u as i32, + syntax::Expr::FloatConst(f) => *f as i32, + syntax::Expr::DoubleConst(f) => *f as i32, + _ => panic!(), + } +} + +fn translate_variable_declaration( + state: &mut State, + d: &syntax::InitDeclaratorList, + default_run_class: RunClass, +) -> Declaration { + let mut ty = d.head.ty.clone(); + ty.ty.array_specifier = d.head.array_specifier.clone(); + let ty_def = match &ty.ty.ty { + TypeSpecifierNonArray::Struct(s) => { + let decl = SymDecl::Struct(lift(state, s)); + Some(state.declare(s.name.as_ref().unwrap().as_str(), decl)) + } + _ => None, + }; + + let mut ty: Type = lift(state, &d.head.ty); + if let Some(array) = &d.head.array_specifier { + ty.array_sizes = Some(Box::new(lift(state, array))) + } + + let (sym, decl) = match d.head.name.as_ref() { + Some(name) => { + let mut storage = StorageClass::None; + let mut interpolation = None; + for qual in d + .head + .ty + .qualifier + .iter() + .flat_map(|x| x.qualifiers.0.iter()) + { + match qual { + syntax::TypeQualifierSpec::Storage(s) => match (&storage, s) { + (StorageClass::FragColor(..), syntax::StorageQualifier::Out) => {} + (StorageClass::Sampler(..), syntax::StorageQualifier::Uniform) => {} + (StorageClass::None, syntax::StorageQualifier::Out) => { + storage = StorageClass::Out; + } + (StorageClass::None, syntax::StorageQualifier::In) => { + storage = StorageClass::In; + } + (StorageClass::None, syntax::StorageQualifier::Uniform) => { + if ty.kind.is_sampler() { + storage = StorageClass::Sampler(SamplerFormat::Unknown); + } else { + storage = StorageClass::Uniform; + } + } + (StorageClass::None, syntax::StorageQualifier::Const) => { + storage = StorageClass::Const; + } + _ => panic!("bad storage {:?}", (storage, s)), + }, + syntax::TypeQualifierSpec::Interpolation(i) => match (&interpolation, i) { + (None, i) => interpolation = Some(i.clone()), + _ => panic!("multiple interpolation"), + }, + syntax::TypeQualifierSpec::Layout(l) => { + let mut loc = -1; + let mut index = -1; + for id in &l.ids { + match id { + syntax::LayoutQualifierSpec::Identifier(ref key, None) => { + match key.as_str() { + "rgba8" => { + storage = StorageClass::Sampler(SamplerFormat::RGBA8); + } + "rgba32f" => { + storage = StorageClass::Sampler(SamplerFormat::RGBA32F); + } + "rgba32i" => { + storage = StorageClass::Sampler(SamplerFormat::RGBA32I); + } + "r8" => { + storage = StorageClass::Sampler(SamplerFormat::R8); + } + _ => {} + } + } + syntax::LayoutQualifierSpec::Identifier(ref key, Some(ref e)) => { + match key.as_str() { + "location" => { + loc = get_expr_index(e); + } + "index" => { + index = get_expr_index(e); + } + _ => {} + } + } + _ => {} + } + } + if index >= 0 { + assert!(loc == 0); + assert!(index <= 1); + assert!(storage == StorageClass::None); + storage = StorageClass::FragColor(index); + } + } + _ => {} + } + } + let decl = if state.in_function.is_some() { + let run_class = match storage { + StorageClass::Const => RunClass::Scalar, + StorageClass::None => default_run_class, + _ => panic!("bad local storage {:?}", storage), + }; + SymDecl::Local(storage, ty.clone(), run_class) + } else { + let run_class = match storage { + StorageClass::Const | StorageClass::Uniform | StorageClass::Sampler(..) => { + RunClass::Scalar + } + StorageClass::In | StorageClass::Out | StorageClass::FragColor(..) + if interpolation == Some(syntax::InterpolationQualifier::Flat) => + { + RunClass::Scalar + } + _ => RunClass::Vector, + }; + SymDecl::Global(storage, interpolation, ty.clone(), run_class) + }; + (state.declare(name.as_str(), decl.clone()), decl) + } + None => panic!(), + }; + + let head = SingleDeclaration { + qualifier: lift_type_qualifier_for_declaration(state, &d.head.ty.qualifier), + name: sym, + ty, + ty_def, + initializer: d + .head + .initializer + .as_ref() + .map(|x| translate_initializater(state, x)), + }; + + let tail = d + .tail + .iter() + .map(|d| { + if let Some(_array) = &d.ident.array_spec { + panic!("unhandled array") + } + state.declare(d.ident.ident.as_str(), decl.clone()); + SingleDeclarationNoType { + ident: d.ident.clone(), + initializer: d + .initializer + .as_ref() + .map(|x| translate_initializater(state, x)), + } + }) + .collect(); + Declaration::InitDeclaratorList(InitDeclaratorList { head, tail }) +} + +fn translate_init_declarator_list( + state: &mut State, + l: &syntax::InitDeclaratorList, + default_run_class: RunClass, +) -> Declaration { + match &l.head.name { + Some(_name) => translate_variable_declaration(state, l, default_run_class), + None => translate_struct_declaration(state, &l.head), + } +} + +fn translate_declaration( + state: &mut State, + d: &syntax::Declaration, + default_run_class: RunClass, +) -> Declaration { + match d { + syntax::Declaration::Block(_) => panic!(), //Declaration::Block(..), + syntax::Declaration::FunctionPrototype(p) => { + Declaration::FunctionPrototype(translate_function_prototype(state, p)) + } + syntax::Declaration::Global(_ty, _ids) => { + panic!(); + // glsl non-es supports requalifying variables + // we don't right now + //Declaration::Global(..) + } + syntax::Declaration::InitDeclaratorList(dl) => { + translate_init_declarator_list(state, dl, default_run_class) + } + syntax::Declaration::Precision(p, ts) => Declaration::Precision(p.clone(), ts.clone()), + } +} + +fn is_vector(ty: &Type) -> bool { + match ty.kind { + TypeKind::Vec2 + | TypeKind::Vec3 + | TypeKind::Vec4 + | TypeKind::IVec2 + | TypeKind::IVec3 + | TypeKind::IVec4 => ty.array_sizes == None, + _ => false, + } +} + +fn index_matrix(ty: &Type) -> Option { + use TypeKind::*; + if ty.array_sizes != None { + return None; + } + Some(match ty.kind { + Mat2 => Vec2, + Mat3 => Vec3, + Mat4 => Vec4, + Mat23 => Vec3, + Mat24 => Vec4, + Mat32 => Vec2, + Mat34 => Vec4, + Mat42 => Vec2, + Mat43 => Vec3, + DMat2 => DVec2, + DMat3 => DVec3, + DMat4 => DVec4, + DMat23 => DVec3, + DMat24 => DVec4, + DMat32 => DVec2, + DMat34 => DVec4, + DMat42 => DVec2, + DMat43 => DVec3, + _ => return None, + }) +} + +fn is_ivec(ty: &Type) -> bool { + match ty.kind { + TypeKind::IVec2 | TypeKind::IVec3 | TypeKind::IVec4 => ty.array_sizes == None, + _ => false, + } +} + +fn compatible_type(lhs: &Type, rhs: &Type) -> bool { + // XXX: use an underlying type helper + if lhs == &Type::new(TypeKind::Double) && rhs == &Type::new(TypeKind::Float) { + true + } else if rhs == &Type::new(TypeKind::Double) && lhs == &Type::new(TypeKind::Float) { + true + } else if rhs == &Type::new(TypeKind::Int) && + (lhs == &Type::new(TypeKind::Float) || lhs == &Type::new(TypeKind::Double)) + { + true + } else if (rhs == &Type::new(TypeKind::Float) || rhs == &Type::new(TypeKind::Double)) && + lhs == &Type::new(TypeKind::Int) + { + true + } else if (rhs == &Type::new(TypeKind::Vec2) || rhs == &Type::new(TypeKind::DVec2)) && + lhs == &Type::new(TypeKind::IVec2) + { + true + } else if rhs == &Type::new(TypeKind::IVec2) && + (lhs == &Type::new(TypeKind::Vec2) || lhs == &Type::new(TypeKind::DVec2)) + { + true + } else { + lhs.kind == rhs.kind && lhs.array_sizes == rhs.array_sizes + } +} + +fn promoted_type(lhs: &Type, rhs: &Type) -> Type { + if lhs == &Type::new(TypeKind::Double) && rhs == &Type::new(TypeKind::Float) { + Type::new(TypeKind::Double) + } else if lhs == &Type::new(TypeKind::Float) && rhs == &Type::new(TypeKind::Double) { + Type::new(TypeKind::Double) + } else if lhs == &Type::new(TypeKind::Int) && rhs == &Type::new(TypeKind::Double) { + Type::new(TypeKind::Double) + } else if is_vector(&lhs) && + (rhs == &Type::new(TypeKind::Float) || + rhs == &Type::new(TypeKind::Double) || + rhs == &Type::new(TypeKind::Int)) + { + // scalars promote to vectors + lhs.clone() + } else if is_vector(&rhs) && + (lhs == &Type::new(TypeKind::Float) || + lhs == &Type::new(TypeKind::Double) || + lhs == &Type::new(TypeKind::Int)) + { + // scalars promote to vectors + rhs.clone() + } else if lhs == rhs { + lhs.clone() + } else if lhs.kind == rhs.kind { + if lhs.array_sizes == rhs.array_sizes { + // XXX: we need to be able to query the default precision here + match (&lhs.precision, &rhs.precision) { + (Some(PrecisionQualifier::High), _) => lhs.clone(), + (_, Some(PrecisionQualifier::High)) => rhs.clone(), + (None, _) => lhs.clone(), + (_, None) => rhs.clone(), + _ => panic!("precision mismatch {:?} {:?}", lhs.precision, rhs.precision), + } + } else { + panic!("array size mismatch") + } + } else { + assert_eq!(lhs, rhs); + lhs.clone() + } +} + +pub fn is_output(expr: &Expr, state: &State) -> Option { + match &expr.kind { + ExprKind::Variable(i) => match state.sym(*i).decl { + SymDecl::Global(storage, ..) => match storage { + StorageClass::Out => return Some(*i), + _ => {} + }, + SymDecl::Local(..) => {} + _ => panic!("should be variable"), + }, + ExprKind::SwizzleSelector(e, ..) => { + return is_output(e, state); + } + ExprKind::Bracket(e, ..) => { + return is_output(e, state); + } + ExprKind::Dot(e, ..) => { + return is_output(e, state); + } + _ => {} + }; + None +} + +pub fn get_texel_fetch_offset( + state: &State, + sampler_expr: &Expr, + uv_expr: &Expr, + offset_expr: &Expr, +) -> Option<(SymRef, SymRef, i32, i32)> { + if let ExprKind::Variable(ref sampler) = &sampler_expr.kind { + //if let ExprKind::Binary(BinaryOp::Add, ref lhs, ref rhs) = &uv_expr.kind { + if let ExprKind::Variable(ref base) = &uv_expr.kind { + if let ExprKind::FunCall(ref fun, ref args) = &offset_expr.kind { + if let FunIdentifier::Identifier(ref offset) = fun { + if state.sym(*offset).name == "ivec2" { + if let ExprKind::IntConst(ref x) = &args[0].kind { + if let ExprKind::IntConst(ref y) = &args[1].kind { + return Some((*sampler, *base, *x, *y)); + } + } + } + } + } + } + //} + } + None +} + +fn translate_expression(state: &mut State, e: &syntax::Expr) -> Expr { + match e { + syntax::Expr::Variable(i) => { + let sym = match state.lookup(i.as_str()) { + Some(sym) => sym, + None => panic!("missing declaration {}", i.as_str()), + }; + let ty = match &state.sym(sym).decl { + SymDecl::Global(_, _, ty, _) => { + let mut globals = state.used_globals.borrow_mut(); + if !globals.contains(&sym) { + globals.push(sym); + } + ty.clone() + } + SymDecl::Local(_, ty, _) => ty.clone(), + _ => panic!("bad variable type"), + }; + Expr { + kind: ExprKind::Variable(sym), + ty, + } + } + syntax::Expr::Assignment(lhs, op, rhs) => { + let lhs = Box::new(translate_expression(state, lhs)); + let rhs = Box::new(translate_expression(state, rhs)); + let ty = if op == &AssignmentOp::Mult { + if lhs.ty.kind == TypeKind::Vec4 && rhs.ty.kind == TypeKind::Float { + lhs.ty.clone() + } else { + promoted_type(&lhs.ty, &rhs.ty) + } + } else { + promoted_type(&lhs.ty, &rhs.ty) + }; + if let Some(global) = is_output(&lhs, state) { + let mut globals = state.modified_globals.borrow_mut(); + if !globals.contains(&global) { + globals.push(global); + } + } + Expr { + kind: ExprKind::Assignment(lhs, op.clone(), rhs), + ty, + } + } + syntax::Expr::Binary(op, lhs, rhs) => { + let lhs = Box::new(translate_expression(state, lhs)); + let rhs = Box::new(translate_expression(state, rhs)); + let ty = if op == &BinaryOp::Mult { + if lhs.ty.kind == TypeKind::Mat3 && rhs.ty.kind == TypeKind::Vec3 { + rhs.ty.clone() + } else if lhs.ty.kind == TypeKind::Mat4 && rhs.ty.kind == TypeKind::Vec4 { + rhs.ty.clone() + } else if lhs.ty.kind == TypeKind::Mat2 && rhs.ty.kind == TypeKind::Vec2 { + rhs.ty.clone() + } else if lhs.ty.kind == TypeKind::Mat2 && rhs.ty.kind == TypeKind::Float { + lhs.ty.clone() + } else { + promoted_type(&lhs.ty, &rhs.ty) + } + } else { + promoted_type(&lhs.ty, &rhs.ty) + }; + + // comparison operators have a bool result + let ty = match op { + BinaryOp::Equal | BinaryOp::GT | BinaryOp::GTE | BinaryOp::LT | BinaryOp::LTE => { + Type::new(TypeKind::Bool) + } + _ => ty, + }; + + Expr { + kind: ExprKind::Binary(op.clone(), lhs, rhs), + ty, + } + } + syntax::Expr::Unary(op, e) => { + let e = Box::new(translate_expression(state, e)); + let ty = e.ty.clone(); + Expr { + kind: ExprKind::Unary(op.clone(), e), + ty, + } + } + syntax::Expr::BoolConst(b) => Expr { + kind: ExprKind::BoolConst(*b), + ty: Type::new(TypeKind::Bool), + }, + syntax::Expr::Comma(lhs, rhs) => { + let lhs = Box::new(translate_expression(state, lhs)); + let rhs = Box::new(translate_expression(state, rhs)); + assert_eq!(lhs.ty, rhs.ty); + let ty = lhs.ty.clone(); + Expr { + kind: ExprKind::Comma(lhs, rhs), + ty, + } + } + syntax::Expr::DoubleConst(d) => Expr { + kind: ExprKind::DoubleConst(*d), + ty: Type::new(TypeKind::Double), + }, + syntax::Expr::FloatConst(f) => Expr { + kind: ExprKind::FloatConst(*f), + ty: Type::new(TypeKind::Float), + }, + syntax::Expr::FunCall(fun, params) => { + let ret_ty: Type; + let params: Vec = params + .iter() + .map(|x| translate_expression(state, x)) + .collect(); + Expr { + kind: ExprKind::FunCall( + match fun { + syntax::FunIdentifier::Identifier(i) => { + let name = i.as_str(); + if name == "texelFetchOffset" && params.len() >= 4 { + if let Some((sampler, base, x, y)) = get_texel_fetch_offset( + state, ¶ms[0], ¶ms[1], ¶ms[3], + ) { + if let Some(offsets) = + state.texel_fetches.get_mut(&(sampler, base)) + { + offsets.add_offset(x, y); + } else { + state + .texel_fetches + .insert((sampler, base), TexelFetchOffsets::new(x, y)); + } + } + } + let sym = match state.lookup(name) { + Some(s) => s, + None => panic!("missing symbol {}", name), + }; + match &state.sym(sym).decl { + SymDecl::NativeFunction(fn_ty, _) => { + let mut ret = None; + for sig in &fn_ty.signatures { + let mut matching = true; + for (e, p) in params.iter().zip(sig.params.iter()) { + if !compatible_type(&e.ty, p) { + matching = false; + break; + } + } + if matching { + ret = Some(sig.ret.clone()); + break; + } + } + ret_ty = match ret { + Some(t) => t, + None => { + dbg!(&fn_ty.signatures); + dbg!(params.iter().map(|p| p).collect::>()); + panic!("no matching func {}", i.as_str()) + } + }; + } + SymDecl::UserFunction(fd, _) => { + let mut globals = state.modified_globals.borrow_mut(); + for global in &fd.globals { + if !globals.contains(global) { + globals.push(*global); + } + } + let mut matching = true; + for (e, p) in params.iter().zip(fd.prototype.parameters.iter()) + { + matching &= match p { + FunctionParameterDeclaration::Named(q, d) => { + match q { + Some(ParameterQualifier::InOut) + | Some(ParameterQualifier::Out) => { + if let Some(global) = is_output(e, state) { + if !globals.contains(&global) { + globals.push(global); + } + } + } + _ => {} + } + compatible_type(&e.ty, &d.ty) + } + FunctionParameterDeclaration::Unnamed(..) => panic!(), + }; + } + assert!(matching); + ret_ty = fd.prototype.ty.clone(); + } + SymDecl::Struct(_) => ret_ty = Type::new(TypeKind::Struct(sym)), + _ => panic!("can only call functions"), + }; + FunIdentifier::Identifier(sym) + } + // array constructor + syntax::FunIdentifier::Expr(e) => { + let ty = match &**e { + syntax::Expr::Bracket(i, array) => { + let kind = match &**i { + syntax::Expr::Variable(i) => match i.as_str() { + "vec4" => TypeKind::Vec4, + "vec2" => TypeKind::Vec2, + _ => panic!("unexpected type constructor {:?}", i), + }, + _ => panic!(), + }; + + Type { + kind, + precision: None, + array_sizes: Some(Box::new(lift(state, array))), + } + } + _ => panic!(), + }; + ret_ty = ty.clone(); + + FunIdentifier::Constructor(ty) + } + }, + params, + ), + ty: ret_ty, + } + } + syntax::Expr::IntConst(i) => Expr { + kind: ExprKind::IntConst(*i), + ty: Type::new(TypeKind::Int), + }, + syntax::Expr::UIntConst(u) => Expr { + kind: ExprKind::UIntConst(*u), + ty: Type::new(TypeKind::UInt), + }, + syntax::Expr::PostDec(e) => { + let e = Box::new(translate_expression(state, e)); + let ty = e.ty.clone(); + Expr { + kind: ExprKind::PostDec(e), + ty, + } + } + syntax::Expr::PostInc(e) => { + let e = Box::new(translate_expression(state, e)); + let ty = e.ty.clone(); + Expr { + kind: ExprKind::PostInc(e), + ty, + } + } + syntax::Expr::Ternary(cond, lhs, rhs) => { + let cond = Box::new(translate_expression(state, cond)); + let lhs = Box::new(translate_expression(state, lhs)); + let rhs = Box::new(translate_expression(state, rhs)); + let ty = promoted_type(&lhs.ty, &rhs.ty); + Expr { + kind: ExprKind::Ternary(cond, lhs, rhs), + ty, + } + } + syntax::Expr::Dot(e, i) => { + let mut e = Box::new(translate_expression(state, e)); + let ty = e.ty.clone(); + let ivec = is_ivec(&ty); + if is_vector(&ty) { + let ty = Type::new(match i.as_str().len() { + 1 => { + if ivec { + TypeKind::Int + } else { + TypeKind::Float + } + } + 2 => { + if ivec { + TypeKind::IVec2 + } else { + TypeKind::Vec2 + } + } + 3 => { + if ivec { + TypeKind::IVec3 + } else { + TypeKind::Vec3 + } + } + 4 => { + if ivec { + TypeKind::IVec4 + } else { + TypeKind::Vec4 + } + } + _ => panic!(), + }); + + let mut sel = SwizzleSelector::parse(i.as_str()); + + if let ExprKind::Variable(ref mut sym) = &mut e.kind { + if state.sym(*sym).name == "gl_FragCoord" { + for c in &sel.components { + state.used_fragcoord |= 1 << c; + } + *sym = state.lookup("gl_FragCoordXY").unwrap(); + for c in &mut sel.components { + if *c >= 2 { + *c -= 2; + *sym = state.lookup("gl_FragCoordZW").unwrap(); + } + } + } + } + + Expr { + kind: ExprKind::SwizzleSelector(e, sel), + ty, + } + } else { + match ty.kind { + TypeKind::Struct(s) => { + let sym = state.sym(s); + let fields = match &sym.decl { + SymDecl::Struct(fields) => fields, + _ => panic!("expected struct"), + }; + let field = fields + .fields + .iter() + .find(|x| &x.name == i) + .expect("missing field"); + Expr { + kind: ExprKind::Dot(e, i.clone()), + ty: field.ty.clone(), + } + } + _ => panic!("expected struct found {:#?} {:#?}", e, ty), + } + } + } + syntax::Expr::Bracket(e, specifier) => { + let e = Box::new(translate_expression(state, e)); + let ty = if is_vector(&e.ty) { + Type::new(TypeKind::Float) + } else if let Some(ty) = index_matrix(&e.ty) { + Type::new(ty) + } else { + let a = match &e.ty.array_sizes { + Some(a) => { + let mut a = *a.clone(); + a.sizes.pop(); + if a.sizes.len() == 0 { + None + } else { + Some(Box::new(a)) + } + } + _ => panic!("{:#?}", e), + }; + Type { + kind: e.ty.kind.clone(), + precision: e.ty.precision.clone(), + array_sizes: a, + } + }; + let indx = match specifier { + ArraySpecifier::Unsized => panic!("need expression"), + ArraySpecifier::ExplicitlySized(e) => translate_expression(state, e), + }; + Expr { + kind: ExprKind::Bracket(e, Box::new(indx)), + ty, + } + } + } +} + +fn translate_switch(state: &mut State, s: &syntax::SwitchStatement) -> SwitchStatement { + let mut cases = Vec::new(); + + let mut case = None; + for stmt in &s.body { + match stmt { + syntax::Statement::Simple(s) => match &**s { + syntax::SimpleStatement::CaseLabel(label) => { + match case.take() { + Some(case) => cases.push(case), + _ => {} + } + case = Some(Case { + label: translate_case(state, &label), + stmts: Vec::new(), + }) + } + _ => match case { + Some(ref mut case) => case.stmts.push(translate_statement(state, stmt)), + _ => panic!("switch must start with case"), + }, + }, + _ => match case { + Some(ref mut case) => case.stmts.push(translate_statement(state, stmt)), + _ => panic!("switch must start with case"), + }, + } + } + match case.take() { + Some(case) => cases.push(case), + _ => {} + } + SwitchStatement { + head: Box::new(translate_expression(state, &s.head)), + cases, + } +} + +fn translate_jump(state: &mut State, s: &syntax::JumpStatement) -> JumpStatement { + match s { + syntax::JumpStatement::Break => JumpStatement::Break, + syntax::JumpStatement::Continue => JumpStatement::Continue, + syntax::JumpStatement::Discard => JumpStatement::Discard, + syntax::JumpStatement::Return(e) => { + JumpStatement::Return(e.as_ref().map(|e| Box::new(translate_expression(state, e)))) + } + } +} + +fn translate_condition(state: &mut State, c: &syntax::Condition) -> Condition { + match c { + syntax::Condition::Expr(e) => Condition::Expr(Box::new(translate_expression(state, e))), + _ => panic!(), + } +} + +fn translate_for_init(state: &mut State, s: &syntax::ForInitStatement) -> ForInitStatement { + match s { + syntax::ForInitStatement::Expression(e) => { + ForInitStatement::Expression(e.as_ref().map(|e| translate_expression(state, e))) + } + syntax::ForInitStatement::Declaration(d) => ForInitStatement::Declaration(Box::new( + translate_declaration(state, d, RunClass::Scalar), + )), + } +} + +fn translate_for_rest(state: &mut State, s: &syntax::ForRestStatement) -> ForRestStatement { + ForRestStatement { + condition: s.condition.as_ref().map(|c| translate_condition(state, c)), + post_expr: s + .post_expr + .as_ref() + .map(|e| Box::new(translate_expression(state, e))), + } +} + +fn translate_iteration(state: &mut State, s: &syntax::IterationStatement) -> IterationStatement { + match s { + syntax::IterationStatement::While(cond, s) => IterationStatement::While( + translate_condition(state, cond), + Box::new(translate_statement(state, s)), + ), + syntax::IterationStatement::For(init, rest, s) => IterationStatement::For( + translate_for_init(state, init), + translate_for_rest(state, rest), + Box::new(translate_statement(state, s)), + ), + syntax::IterationStatement::DoWhile(s, e) => IterationStatement::DoWhile( + Box::new(translate_statement(state, s)), + Box::new(translate_expression(state, e)), + ), + } +} + +fn translate_case(state: &mut State, c: &syntax::CaseLabel) -> CaseLabel { + match c { + syntax::CaseLabel::Def => CaseLabel::Def, + syntax::CaseLabel::Case(e) => CaseLabel::Case(Box::new(translate_expression(state, e))), + } +} + +fn translate_selection_rest( + state: &mut State, + s: &syntax::SelectionRestStatement, +) -> (Box, Option>) { + match s { + syntax::SelectionRestStatement::Statement(s) => { + (Box::new(translate_statement(state, s)), None) + } + syntax::SelectionRestStatement::Else(if_body, rest) => ( + Box::new(translate_statement(state, if_body)), + Some(Box::new(translate_statement(state, rest))), + ), + } +} + +fn translate_selection(state: &mut State, s: &syntax::SelectionStatement) -> SelectionStatement { + let cond = Box::new(translate_expression(state, &s.cond)); + let (body, else_stmt) = translate_selection_rest(state, &s.rest); + SelectionStatement { + cond, + body, + else_stmt, + } +} + +fn translate_simple_statement(state: &mut State, s: &syntax::SimpleStatement) -> SimpleStatement { + match s { + syntax::SimpleStatement::Declaration(d) => { + SimpleStatement::Declaration(translate_declaration(state, d, RunClass::Unknown)) + } + syntax::SimpleStatement::Expression(e) => { + SimpleStatement::Expression(e.as_ref().map(|e| translate_expression(state, e))) + } + syntax::SimpleStatement::Iteration(i) => { + SimpleStatement::Iteration(translate_iteration(state, i)) + } + syntax::SimpleStatement::Selection(s) => { + SimpleStatement::Selection(translate_selection(state, s)) + } + syntax::SimpleStatement::Jump(j) => SimpleStatement::Jump(translate_jump(state, j)), + syntax::SimpleStatement::Switch(s) => SimpleStatement::Switch(translate_switch(state, s)), + syntax::SimpleStatement::CaseLabel(_) => panic!("should be handled by translate_switch"), + } +} + +fn translate_statement(state: &mut State, s: &syntax::Statement) -> Statement { + match s { + syntax::Statement::Compound(s) => { + Statement::Compound(Box::new(translate_compound_statement(state, s))) + } + syntax::Statement::Simple(s) => { + Statement::Simple(Box::new(translate_simple_statement(state, s))) + } + } +} + +fn translate_compound_statement( + state: &mut State, + cs: &syntax::CompoundStatement, +) -> CompoundStatement { + CompoundStatement { + statement_list: cs + .statement_list + .iter() + .map(|x| translate_statement(state, x)) + .collect(), + } +} + +fn translate_function_parameter_declaration( + state: &mut State, + p: &syntax::FunctionParameterDeclaration, + index: usize, +) -> FunctionParameterDeclaration { + match p { + syntax::FunctionParameterDeclaration::Named(qual, p) => { + let mut ty: Type = lift(state, &p.ty); + if let Some(a) = &p.ident.array_spec { + ty.array_sizes = Some(Box::new(lift(state, a))); + } + + ty.precision = get_precision(qual); + + let decl = SymDecl::Local( + StorageClass::None, + ty.clone(), + RunClass::Dependent(1 << index), + ); + let d = FunctionParameterDeclarator { + ty, + name: p.ident.ident.clone(), + sym: state.declare(p.ident.ident.as_str(), decl), + }; + FunctionParameterDeclaration::Named(lift_type_qualifier_for_parameter(state, qual), d) + } + syntax::FunctionParameterDeclaration::Unnamed(qual, p) => { + FunctionParameterDeclaration::Unnamed( + lift_type_qualifier_for_parameter(state, qual), + p.clone(), + ) + } + } +} + +fn translate_prototype( + state: &mut State, + cs: &syntax::FunctionPrototype, +) -> (FunctionPrototype, SymRef) { + let prototype = FunctionPrototype { + ty: lift(state, &cs.ty), + name: cs.name.clone(), + parameters: cs + .parameters + .iter() + .enumerate() + .map(|(i, x)| translate_function_parameter_declaration(state, x, i)) + .collect(), + }; + let sym = if let Some(sym) = state.lookup(prototype.name.as_str()) { + match &state.sym(sym).decl { + SymDecl::UserFunction(..) => {} + _ => panic!( + "prototype conflicts with existing symbol: {}", + prototype.name.as_str() + ), + } + sym + } else { + let pfd = Rc::new(FunctionDefinition { + prototype: prototype.clone(), + body: CompoundStatement::new(), + globals: Vec::new(), + texel_fetches: HashMap::new(), + }); + state.declare( + prototype.name.as_str(), + SymDecl::UserFunction(pfd, RunClass::Unknown), + ) + }; + (prototype, sym) +} + +fn translate_function_prototype( + state: &mut State, + prototype: &syntax::FunctionPrototype, +) -> FunctionPrototype { + let (prototype, _) = translate_prototype(state, prototype); + prototype +} + +fn translate_function_definition( + state: &mut State, + sfd: &syntax::FunctionDefinition, +) -> Rc { + let (prototype, sym) = translate_prototype(state, &sfd.prototype); + + state.push_scope(prototype.name.as_str().into()); + state.in_function = Some(sym); + state.modified_globals.get_mut().clear(); + state.texel_fetches.clear(); + let body = translate_compound_statement(state, &sfd.statement); + let mut globals = Vec::new(); + mem::swap(&mut globals, state.modified_globals.get_mut()); + let mut texel_fetches = HashMap::new(); + mem::swap(&mut texel_fetches, &mut state.texel_fetches); + state.in_function = None; + state.pop_scope(); + + let fd = Rc::new(FunctionDefinition { + prototype, + body, + globals, + texel_fetches, + }); + state.sym_mut(sym).decl = SymDecl::UserFunction(fd.clone(), RunClass::Unknown); + fd +} + +fn translate_external_declaration( + state: &mut State, + ed: &syntax::ExternalDeclaration, +) -> ExternalDeclaration { + match ed { + syntax::ExternalDeclaration::Declaration(d) => { + ExternalDeclaration::Declaration(translate_declaration(state, d, RunClass::Unknown)) + } + syntax::ExternalDeclaration::FunctionDefinition(fd) => { + ExternalDeclaration::FunctionDefinition(translate_function_definition(state, fd)) + } + syntax::ExternalDeclaration::Preprocessor(p) => { + ExternalDeclaration::Preprocessor(p.clone()) + } + } +} + +fn declare_function( + state: &mut State, + name: &str, + cxx_name: Option<&'static str>, + ret: Type, + params: Vec, +) { + let sig = FunctionSignature { ret, params }; + match state.lookup_sym_mut(name) { + Some(Symbol { + decl: SymDecl::NativeFunction(f, ..), + .. + }) => f.signatures.push(sig), + None => { + state.declare( + name, + SymDecl::NativeFunction( + FunctionType { + signatures: NonEmpty::new(sig), + }, + cxx_name, + ), + ); + } + _ => panic!("overloaded function name {}", name), + } + //state.declare(name, Type::Function(FunctionType{ v})) +} + +pub fn ast_to_hir(state: &mut State, tu: &syntax::TranslationUnit) -> TranslationUnit { + // global scope + state.push_scope("global".into()); + use TypeKind::*; + declare_function( + state, + "vec2", + Some("make_vec2"), + Type::new(Vec2), + vec![Type::new(Float)], + ); + declare_function( + state, + "vec2", + Some("make_vec2"), + Type::new(Vec2), + vec![Type::new(IVec2)], + ); + declare_function( + state, + "vec2", + Some("make_vec2"), + Type::new(Vec2), + vec![Type::new(IVec3)], + ); + declare_function( + state, + "vec3", + Some("make_vec3"), + Type::new(Vec3), + vec![Type::new(Float), Type::new(Float), Type::new(Float)], + ); + declare_function( + state, + "vec3", + Some("make_vec3"), + Type::new(Vec3), + vec![Type::new(Float)], + ); + declare_function( + state, + "vec3", + Some("make_vec3"), + Type::new(Vec3), + vec![Type::new(Vec2), Type::new(Float)], + ); + declare_function( + state, + "vec4", + Some("make_vec4"), + Type::new(Vec4), + vec![Type::new(Vec3), Type::new(Float)], + ); + declare_function( + state, + "vec4", + Some("make_vec4"), + Type::new(Vec4), + vec![ + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + ], + ); + declare_function( + state, + "vec4", + Some("make_vec4"), + Type::new(Vec4), + vec![Type::new(Vec2), Type::new(Float), Type::new(Float)], + ); + declare_function( + state, + "vec4", + Some("make_vec4"), + Type::new(Vec4), + vec![Type::new(Vec2), Type::new(Vec2)], + ); + declare_function( + state, + "vec4", + Some("make_vec4"), + Type::new(Vec4), + vec![Type::new(Float), Type::new(Float), Type::new(Vec2)], + ); + declare_function( + state, + "vec4", + Some("make_vec4"), + Type::new(Vec4), + vec![Type::new(Vec4)], + ); + + declare_function( + state, + "bvec2", + Some("make_bvec2"), + Type::new(BVec2), + vec![Type::new(UInt)], + ); + declare_function( + state, + "bvec4", + Some("make_bvec4"), + Type::new(BVec4), + vec![Type::new(BVec2), Type::new(BVec2)], + ); + + declare_function( + state, + "int", + Some("make_int"), + Type::new(Int), + vec![Type::new(Float)], + ); + declare_function( + state, + "float", + Some("make_float"), + Type::new(Float), + vec![Type::new(Float)], + ); + declare_function( + state, + "float", + Some("make_float"), + Type::new(Float), + vec![Type::new(Bool)], + ); + declare_function( + state, + "int", + Some("make_int"), + Type::new(Int), + vec![Type::new(UInt)], + ); + declare_function( + state, + "uint", + Some("make_uint"), + Type::new(UInt), + vec![Type::new(Float)], + ); + declare_function( + state, + "uint", + Some("make_uint"), + Type::new(UInt), + vec![Type::new(Int)], + ); + declare_function( + state, + "ivec2", + Some("make_ivec2"), + Type::new(IVec2), + vec![Type::new(UInt), Type::new(UInt)], + ); + declare_function( + state, + "ivec2", + Some("make_ivec2"), + Type::new(IVec2), + vec![Type::new(Int), Type::new(Int)], + ); + declare_function( + state, + "ivec2", + Some("make_ivec2"), + Type::new(IVec2), + vec![Type::new(Vec2)], + ); + declare_function( + state, + "ivec3", + Some("make_ivec3"), + Type::new(IVec3), + vec![Type::new(IVec2), Type::new(Int)], + ); + declare_function( + state, + "ivec4", + Some("make_ivec4"), + Type::new(IVec4), + vec![ + Type::new(Int), + Type::new(Int), + Type::new(Int), + Type::new(Int), + ], + ); + declare_function( + state, + "ivec4", + Some("make_ivec4"), + Type::new(IVec4), + vec![Type::new(IVec2), Type::new(Int), Type::new(Int)], + ); + + declare_function( + state, + "mat2", + Some("make_mat2"), + Type::new(Mat2), + vec![Type::new(Vec2), Type::new(Vec2)], + ); + declare_function( + state, + "mat2", + Some("make_mat2"), + Type::new(Mat2), + vec![Type::new(Float)], + ); + declare_function( + state, + "mat2", + Some("make_mat2"), + Type::new(Mat2), + vec![Type::new(Mat4)], + ); + declare_function( + state, + "mat3", + Some("make_mat3"), + Type::new(Mat3), + vec![Type::new(Vec3), Type::new(Vec3), Type::new(Vec3)], + ); + declare_function( + state, + "mat3", + Some("make_mat3"), + Type::new(Mat3), + vec![Type::new(Mat4)], + ); + declare_function( + state, + "mat3", + Some("make_mat3"), + Type::new(Mat3), + vec![ + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + ], + ); + declare_function( + state, + "mat4", + Some("make_mat4"), + Type::new(Mat4), + vec![ + Type::new(Vec4), + Type::new(Vec4), + Type::new(Vec4), + Type::new(Vec4), + ], + ); + declare_function( + state, + "mat4", + Some("make_mat4"), + Type::new(Mat4), + vec![ + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + ], + ); + declare_function(state, "abs", None, Type::new(Vec2), vec![Type::new(Vec2)]); + declare_function(state, "abs", None, Type::new(Vec3), vec![Type::new(Vec3)]); + declare_function(state, "abs", None, Type::new(Float), vec![Type::new(Float)]); + declare_function( + state, + "dot", + None, + Type::new(Float), + vec![Type::new(Vec3), Type::new(Vec3)], + ); + declare_function( + state, + "dot", + None, + Type::new(Float), + vec![Type::new(Vec2), Type::new(Vec2)], + ); + declare_function( + state, + "min", + None, + Type::new(Float), + vec![Type::new(Float), Type::new(Float)], + ); + declare_function( + state, + "min", + None, + Type::new(Vec2), + vec![Type::new(Vec2), Type::new(Vec2)], + ); + declare_function( + state, + "min", + None, + Type::new(Vec3), + vec![Type::new(Vec3), Type::new(Vec3)], + ); + + declare_function( + state, + "max", + None, + Type::new(Float), + vec![Type::new(Float), Type::new(Float)], + ); + declare_function( + state, + "max", + None, + Type::new(Vec2), + vec![Type::new(Vec2), Type::new(Vec2)], + ); + declare_function( + state, + "max", + None, + Type::new(Vec2), + vec![Type::new(Vec2), Type::new(Float)], + ); + declare_function( + state, + "max", + None, + Type::new(Vec3), + vec![Type::new(Vec3), Type::new(Vec3)], + ); + + declare_function( + state, + "mix", + None, + Type::new(Vec2), + vec![Type::new(Vec2), Type::new(Vec2), Type::new(Vec2)], + ); + declare_function( + state, + "mix", + None, + Type::new(Vec2), + vec![Type::new(Vec2), Type::new(Vec2), Type::new(BVec2)], + ); + declare_function( + state, + "mix", + None, + Type::new(Vec2), + vec![Type::new(Vec2), Type::new(Vec2), Type::new(Float)], + ); + declare_function( + state, + "mix", + None, + Type::new(Vec3), + vec![Type::new(Vec3), Type::new(Vec3), Type::new(Vec3)], + ); + declare_function( + state, + "mix", + None, + Type::new(Vec4), + vec![Type::new(Vec4), Type::new(Vec4), Type::new(Vec4)], + ); + declare_function( + state, + "mix", + None, + Type::new(Vec4), + vec![Type::new(Vec4), Type::new(Vec4), Type::new(Float)], + ); + declare_function( + state, + "mix", + None, + Type::new(Vec3), + vec![Type::new(Vec3), Type::new(Vec3), Type::new(Float)], + ); + declare_function( + state, + "mix", + None, + Type::new(Vec3), + vec![Type::new(Vec3), Type::new(Vec3), Type::new(BVec3)], + ); + declare_function( + state, + "mix", + None, + Type::new(Float), + vec![Type::new(Float), Type::new(Float), Type::new(Float)], + ); + declare_function( + state, + "mix", + None, + Type::new(Vec4), + vec![Type::new(Vec4), Type::new(Vec4), Type::new(BVec4)], + ); + declare_function( + state, + "step", + None, + Type::new(Float), + vec![Type::new(Float), Type::new(Float)], + ); + declare_function( + state, + "step", + None, + Type::new(Vec2), + vec![Type::new(Vec2), Type::new(Vec2)], + ); + declare_function( + state, + "step", + None, + Type::new(Vec3), + vec![Type::new(Vec3), Type::new(Vec3)], + ); + declare_function( + state, + "notEqual", + None, + Type::new(BVec4), + vec![Type::new(IVec4), Type::new(IVec4)], + ); + + declare_function( + state, + "fwidth", + None, + Type::new(Vec2), + vec![Type::new(Vec2)], + ); + 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, + "clamp", + None, + Type::new(Vec3), + vec![Type::new(Vec3), Type::new(Float), Type::new(Float)], + ); + declare_function( + state, + "clamp", + None, + Type::new(Double), + vec![Type::new(Double), Type::new(Double), Type::new(Double)], + ); + declare_function( + state, + "clamp", + None, + Type::new(Vec2), + vec![Type::new(Vec2), Type::new(Vec2), Type::new(Vec2)], + ); + declare_function( + state, + "clamp", + None, + Type::new(Vec3), + vec![Type::new(Vec3), Type::new(Vec3), Type::new(Vec3)], + ); + declare_function( + state, + "clamp", + None, + Type::new(Vec4), + vec![Type::new(Vec4), Type::new(Vec4), Type::new(Vec4)], + ); + declare_function( + state, + "length", + None, + Type::new(Float), + vec![Type::new(Vec2)], + ); + declare_function(state, "pow", None, Type::new(Vec3), vec![Type::new(Vec3)]); + declare_function(state, "pow", None, Type::new(Float), vec![Type::new(Float)]); + declare_function(state, "exp", None, Type::new(Float), vec![Type::new(Float)]); + declare_function( + state, + "inversesqrt", + None, + Type::new(Float), + vec![Type::new(Float)], + ); + declare_function( + state, + "sqrt", + None, + Type::new(Float), + vec![Type::new(Float)], + ); + declare_function( + state, + "distance", + None, + Type::new(Float), + vec![Type::new(Vec2), Type::new(Vec2)], + ); + + declare_function( + state, + "lessThanEqual", + None, + Type::new(BVec2), + vec![Type::new(Vec2), Type::new(Vec2)], + ); + declare_function( + state, + "lessThanEqual", + None, + Type::new(BVec3), + vec![Type::new(Vec3), Type::new(Vec3)], + ); + declare_function( + state, + "lessThanEqual", + None, + Type::new(BVec4), + vec![Type::new(Vec4), Type::new(Vec4)], + ); + declare_function( + state, + "lessThan", + None, + Type::new(BVec2), + vec![Type::new(Vec2), Type::new(Vec2)], + ); + declare_function( + state, + "greaterThan", + None, + Type::new(BVec2), + vec![Type::new(Vec2), Type::new(Vec2)], + ); + declare_function( + state, + "greaterThanEqual", + None, + Type::new(BVec2), + vec![Type::new(Vec2), Type::new(Vec2)], + ); + declare_function( + state, + "greaterThanEqual", + None, + Type::new(BVec2), + vec![Type::new(Vec4), Type::new(Vec4)], + ); + declare_function(state, "any", None, Type::new(Bool), vec![Type::new(BVec2)]); + declare_function(state, "all", None, Type::new(Bool), vec![Type::new(BVec2)]); + declare_function(state, "all", None, Type::new(Bool), vec![Type::new(BVec4)]); + + declare_function( + state, + "if_then_else", + None, + Type::new(Vec3), + vec![Type::new(BVec3), Type::new(Vec3), Type::new(Vec3)], + ); + declare_function(state, "floor", None, Type::new(Vec4), vec![Type::new(Vec4)]); + declare_function(state, "floor", None, Type::new(Vec2), vec![Type::new(Vec2)]); + declare_function( + state, + "floor", + None, + Type::new(Double), + vec![Type::new(Double)], + ); + declare_function( + state, + "ceil", + None, + Type::new(Double), + vec![Type::new(Double)], + ); + declare_function( + state, + "round", + None, + Type::new(Double), + vec![Type::new(Double)], + ); + declare_function( + state, + "fract", + None, + Type::new(Float), + vec![Type::new(Float)], + ); + declare_function(state, "mod", None, Type::new(Vec2), vec![Type::new(Vec2)]); + declare_function(state, "mod", None, Type::new(Float), vec![Type::new(Float)]); + + declare_function( + state, + "texelFetch", + None, + Type::new(Vec4), + vec![Type::new(Sampler2D), Type::new(IVec2), Type::new(Int)], + ); + declare_function( + state, + "texelFetch", + None, + Type::new(Vec4), + vec![Type::new(Sampler2DArray), Type::new(IVec3), Type::new(Int)], + ); + declare_function( + state, + "texelFetch", + None, + Type::new(IVec4), + vec![Type::new(ISampler2D), Type::new(IVec2), Type::new(Int)], + ); + declare_function( + state, + "texelFetchOffset", + None, + Type::new(Vec4), + vec![ + Type::new(Sampler2D), + Type::new(IVec2), + Type::new(Int), + Type::new(IVec2), + ], + ); + declare_function( + state, + "texelFetchOffset", + None, + Type::new(IVec4), + vec![ + Type::new(ISampler2D), + Type::new(IVec2), + Type::new(Int), + Type::new(IVec2), + ], + ); + declare_function( + state, + "texture", + None, + Type::new(Vec4), + vec![Type::new(Sampler2D), Type::new(Vec3)], + ); + declare_function( + state, + "texture", + None, + Type::new(Vec4), + vec![Type::new(Sampler2D), Type::new(Vec2)], + ); + declare_function( + state, + "texture", + None, + Type::new(Vec4), + vec![Type::new(Sampler2DArray), Type::new(Vec3)], + ); + declare_function( + state, + "textureLod", + None, + Type::new(Vec4), + vec![Type::new(Sampler2DArray), Type::new(Vec3), Type::new(Float)], + ); + declare_function( + state, + "textureSize", + None, + Type::new(IVec3), + vec![Type::new(Sampler2DArray), Type::new(Int)], + ); + declare_function( + state, + "textureSize", + None, + Type::new(IVec2), + vec![Type::new(Sampler2D), Type::new(Int)], + ); + + declare_function( + state, + "inverse", + None, + Type::new(Mat2), + vec![Type::new(Mat2)], + ); + declare_function( + state, + "transpose", + None, + Type::new(Mat3), + vec![Type::new(Mat3)], + ); + declare_function( + state, + "normalize", + None, + Type::new(Vec2), + vec![Type::new(Vec2)], + ); + state.declare( + "gl_FragCoord", + SymDecl::Global(StorageClass::In, None, Type::new(Vec4), RunClass::Vector), + ); + state.declare( + "gl_FragCoordXY", + SymDecl::Global(StorageClass::In, None, Type::new(Vec2), RunClass::Vector), + ); + state.declare( + "gl_FragCoordZW", + SymDecl::Global(StorageClass::In, None, Type::new(Vec2), RunClass::Scalar), + ); + state.declare( + "gl_FragColor", + SymDecl::Global(StorageClass::Out, None, Type::new(Vec4), RunClass::Vector), + ); + state.declare( + "gl_Position", + SymDecl::Global(StorageClass::Out, None, Type::new(Vec4), RunClass::Vector), + ); + + TranslationUnit(tu.0.map(state, translate_external_declaration)) +} + +fn infer_expr_inner(state: &mut State, expr: &Expr, assign: &mut SymRef) -> RunClass { + match expr.kind { + ExprKind::Variable(ref i) => { + *assign = *i; + match &state.sym(*i).decl { + SymDecl::Local(_, _, ref run_class) => *run_class, + SymDecl::Global(_, _, _, ref run_class) => *run_class, + _ => panic!(), + } + } + ExprKind::IntConst(_) + | ExprKind::UIntConst(_) + | ExprKind::BoolConst(_) + | ExprKind::FloatConst(_) + | ExprKind::DoubleConst(_) => RunClass::Scalar, + ExprKind::Unary(_, ref e) => infer_expr(state, e), + ExprKind::Binary(_, ref l, ref r) => infer_expr(state, l).merge(infer_expr(state, r)), + ExprKind::Ternary(ref c, ref s, ref e) => infer_expr(state, c) + .merge(infer_expr(state, s)) + .merge(infer_expr(state, e)), + ExprKind::Assignment(ref v, _, ref e) => { + let mut sym = SymRef(!0); + let run_class = infer_expr_inner(state, v, &mut sym).merge(infer_expr(state, e)); + assert!(sym != SymRef(!0)); + state.merge_run_class(sym, run_class) + } + ExprKind::Bracket(ref e, ref indx) => { + infer_expr_inner(state, e, assign).merge(infer_expr(state, indx)) + } + ExprKind::FunCall(ref fun, ref args) => { + let arg_classes: Vec<(RunClass, SymRef)> = args + .iter() + .map(|e| { + let mut assign = SymRef(!0); + let run_class = infer_expr_inner(state, e, &mut assign); + (run_class, assign) + }) + .collect(); + let run_class = if args.is_empty() { + RunClass::Scalar + } else { + arg_classes + .iter() + .fold(RunClass::Unknown, |x, &(y, _)| x.merge(y)) + }; + match fun { + FunIdentifier::Identifier(ref sym) => match &state.sym(*sym).decl { + SymDecl::NativeFunction(..) => run_class, + SymDecl::UserFunction(ref fd, ref run_class) => { + for (&(mut arg_class, assign), param) in + arg_classes.iter().zip(fd.prototype.parameters.iter()) + { + if let FunctionParameterDeclaration::Named(Some(qual), p) = param { + match qual { + ParameterQualifier::InOut | ParameterQualifier::Out => { + if let SymDecl::Local(_, _, param_class) = + &state.sym(p.sym).decl + { + match param_class { + RunClass::Unknown | RunClass::Vector => { + arg_class = RunClass::Vector; + } + RunClass::Dependent(mask) => { + for i in 0 .. 31 { + if (mask & (1 << i)) != 0 { + arg_class = + arg_class.merge(arg_classes[i].0); + } + } + } + RunClass::Scalar => {} + } + } + assert!(assign != SymRef(!0)); + state.merge_run_class(assign, arg_class); + } + _ => {} + } + } + } + if fd.prototype.ty.kind == TypeKind::Void { + RunClass::Scalar + } else { + match *run_class { + RunClass::Unknown | RunClass::Vector => RunClass::Vector, + RunClass::Dependent(mask) => { + let mut ret_class = RunClass::Unknown; + for i in 0 .. 31 { + if (mask & (1 << i)) != 0 { + ret_class = ret_class.merge(arg_classes[i].0); + } + } + ret_class + } + RunClass::Scalar => RunClass::Scalar, + } + } + } + SymDecl::Struct(..) => run_class, + _ => panic!(), + }, + FunIdentifier::Constructor(..) => run_class, + } + } + ExprKind::Dot(ref e, _) => infer_expr_inner(state, e, assign), + ExprKind::SwizzleSelector(ref e, _) => infer_expr_inner(state, e, assign), + ExprKind::PostInc(ref e) => infer_expr_inner(state, e, assign), + ExprKind::PostDec(ref e) => infer_expr_inner(state, e, assign), + ExprKind::Comma(ref a, ref b) => { + infer_expr(state, a); + infer_expr(state, b) + } + ExprKind::Cond(_, ref e) => infer_expr(state, e), + ExprKind::CondMask => RunClass::Vector, + } +} + +fn infer_expr(state: &mut State, expr: &Expr) -> RunClass { + infer_expr_inner(state, expr, &mut SymRef(!0)) +} + +fn infer_condition(state: &mut State, c: &Condition) { + match *c { + Condition::Expr(ref e) => { + infer_expr(state, e); + } + } +} + +fn infer_iteration_statement(state: &mut State, ist: &IterationStatement) { + let changed = state.run_class_changed.replace(true); + match *ist { + IterationStatement::While(ref cond, ref body) => { + while state.run_class_changed.replace(false) { + infer_condition(state, cond); + infer_statement(state, body); + } + } + IterationStatement::DoWhile(ref body, ref cond) => { + while state.run_class_changed.replace(false) { + infer_statement(state, body); + infer_expr(state, cond); + } + } + IterationStatement::For(ref init, ref rest, ref body) => { + match *init { + ForInitStatement::Expression(ref expr) => { + if let Some(ref e) = *expr { + infer_expr(state, e); + } + } + ForInitStatement::Declaration(ref d) => { + infer_declaration(state, d); + } + } + while state.run_class_changed.replace(false) { + if let Some(ref cond) = rest.condition { + infer_condition(state, cond); + } + if let Some(ref e) = rest.post_expr { + infer_expr(state, e); + } + infer_statement(state, body); + } + } + } + state.run_class_changed.set(changed); +} + +fn infer_selection_statement(state: &mut State, sst: &SelectionStatement) { + let mut branch_run_class = state.branch_run_class.merge(infer_expr(state, &sst.cond)); + mem::swap(&mut state.branch_run_class, &mut branch_run_class); + let branch_declaration = state.branch_declaration; + state.branch_declaration = state.last_declaration; + infer_statement(state, &sst.body); + if let Some(ref else_st) = sst.else_stmt { + infer_statement(state, else_st); + } + state.branch_run_class = branch_run_class; + state.branch_declaration = branch_declaration; +} + +fn infer_expression_statement(state: &mut State, est: &ExprStatement) { + if let Some(ref e) = *est { + infer_expr(state, e); + } +} + +fn infer_switch_statement(state: &mut State, sst: &SwitchStatement) { + let mut branch_run_class = state.branch_run_class.merge(infer_expr(state, &sst.head)); + mem::swap(&mut state.branch_run_class, &mut branch_run_class); + let branch_declaration = state.branch_declaration; + state.branch_declaration = state.last_declaration; + for case in &sst.cases { + for st in &case.stmts { + infer_statement(state, st); + } + } + state.branch_run_class = branch_run_class; + state.branch_declaration = branch_declaration; +} + +fn infer_jump_statement(state: &mut State, j: &JumpStatement) { + match *j { + JumpStatement::Continue => {} + JumpStatement::Break => {} + JumpStatement::Discard => {} + JumpStatement::Return(ref e) => { + if let Some(e) = e { + let run_class = infer_expr(state, e); + state.return_run_class(run_class); + } + } + } +} + +fn infer_initializer(state: &mut State, i: &Initializer) -> RunClass { + match *i { + Initializer::Simple(ref e) => infer_expr(state, e), + Initializer::List(ref list) => { + let mut run_class = RunClass::Unknown; + for ini in list.0.iter() { + run_class = run_class.merge(infer_initializer(state, ini)); + } + run_class + } + } +} + +fn infer_declaration(state: &mut State, d: &Declaration) { + match *d { + Declaration::FunctionPrototype(..) => {} + Declaration::InitDeclaratorList(ref list) => { + state.last_declaration = list.head.name; + + let mut run_class = RunClass::Unknown; + for decl in &list.tail { + if let Some(ref initializer) = decl.initializer { + run_class = run_class.merge(infer_initializer(state, initializer)); + } + } + if let Some(ref initializer) = list.head.initializer { + run_class = run_class.merge(infer_initializer(state, initializer)); + state.merge_run_class(list.head.name, run_class); + } + } + Declaration::Precision(..) => {} + Declaration::Block(..) => {} + Declaration::Global(..) => {} + Declaration::StructDefinition(..) => {} + } +} + +fn infer_simple_statement(state: &mut State, sst: &SimpleStatement) { + match *sst { + SimpleStatement::Declaration(ref d) => infer_declaration(state, d), + SimpleStatement::Expression(ref e) => infer_expression_statement(state, e), + SimpleStatement::Selection(ref s) => infer_selection_statement(state, s), + SimpleStatement::Switch(ref s) => infer_switch_statement(state, s), + SimpleStatement::Iteration(ref i) => infer_iteration_statement(state, i), + SimpleStatement::Jump(ref j) => infer_jump_statement(state, j), + } +} + +fn infer_compound_statement(state: &mut State, cst: &CompoundStatement) { + for st in &cst.statement_list { + infer_statement(state, st); + } +} + +fn infer_statement(state: &mut State, st: &Statement) { + match *st { + Statement::Compound(ref cst) => infer_compound_statement(state, cst), + Statement::Simple(ref sst) => infer_simple_statement(state, sst), + } +} + +fn infer_function_definition(state: &mut State, fd: &FunctionDefinition) { + state.in_function = Some(state.lookup(fd.prototype.name.as_str()).unwrap()); + + state.run_class_changed.set(true); + while state.run_class_changed.replace(false) { + for st in &fd.body.statement_list { + infer_statement(state, st); + } + } + + state.in_function = None; +} + +fn infer_external_declaration(state: &mut State, ed: &ExternalDeclaration) { + match *ed { + ExternalDeclaration::Preprocessor(_) => {} + ExternalDeclaration::FunctionDefinition(ref fd) => infer_function_definition(state, fd), + ExternalDeclaration::Declaration(_) => {} + } +} + +pub fn infer_run_class(state: &mut State, tu: &TranslationUnit) { + for ed in &(tu.0).0 { + infer_external_declaration(state, ed); + } +} diff --git a/glsl-to-cxx/src/lib.rs b/glsl-to-cxx/src/lib.rs new file mode 100644 index 0000000000..7ce461abf0 --- /dev/null +++ b/glsl-to-cxx/src/lib.rs @@ -0,0 +1,3689 @@ +/* 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/. */ + +extern crate glsl; + +mod hir; + +use glsl::parser::Parse; +use glsl::syntax; +use glsl::syntax::{TranslationUnit, UnaryOp}; +use hir::{Statement, SwizzleSelector, Type}; +use std::cell::{Cell, RefCell}; +use std::collections::{BTreeMap, HashMap}; +use std::io::Read; +use std::mem; + +#[derive(PartialEq, Eq)] +enum ShaderKind { + Fragment, + Vertex, +} + +type UniformIndices = BTreeMap; + +fn build_uniform_indices(indices: &mut UniformIndices, state: &hir::State) { + for u in state.used_globals.borrow().iter() { + let sym = state.sym(*u); + match &sym.decl { + hir::SymDecl::Global(storage, _, ty, _) => match storage { + hir::StorageClass::Uniform | hir::StorageClass::Sampler(..) => { + let next_index = indices.len() as i32 + 1; + indices.entry(sym.name.clone()).or_insert(( + next_index, + ty.kind.clone(), + *storage, + )); + } + _ => {} + }, + _ => {} + } + } +} + +pub fn translate(args: &mut dyn Iterator) -> String { + let _cmd_name = args.next(); + let vertex_file = args.next().unwrap(); + + let vs_name = std::path::Path::new(&vertex_file) + .file_stem() + .unwrap() + .to_string_lossy() + .to_string(); + + let frag_file = args.next().unwrap(); + + let fs_name = std::path::Path::new(&frag_file) + .file_stem() + .unwrap() + .to_string_lossy() + .to_string(); + + let frag_include = args.next(); + + let (vs_state, vs_hir, vs_is_frag) = parse_shader(vertex_file); + let (fs_state, fs_hir, fs_is_frag) = parse_shader(frag_file); + + // we use a BTree so that iteration is stable + let mut uniform_indices = BTreeMap::new(); + build_uniform_indices(&mut uniform_indices, &vs_state); + build_uniform_indices(&mut uniform_indices, &fs_state); + + assert_eq!(fs_name, vs_name); + + let mut result = translate_shader( + vs_name, + vs_state, + vs_hir, + vs_is_frag, + &uniform_indices, + None, + ); + result += "\n"; + result += &translate_shader( + fs_name, + fs_state, + fs_hir, + fs_is_frag, + &uniform_indices, + frag_include, + ); + result +} + +fn parse_shader(file: String) -> (hir::State, hir::TranslationUnit, bool) { + let mut contents = String::new(); + let is_frag = file.contains("frag"); + std::fs::File::open(&file) + .unwrap() + .read_to_string(&mut contents) + .unwrap(); + let r = TranslationUnit::parse(contents); + + //println!("{:#?}", r); + let mut ast_glsl = String::new(); + let r = r.unwrap(); + glsl::transpiler::glsl::show_translation_unit(&mut ast_glsl, &r); + //let mut fast = std::fs::File::create("ast").unwrap(); + //fast.write(ast_glsl.as_bytes()); + + let mut state = hir::State::new(); + let hir = hir::ast_to_hir(&mut state, &r); + (state, hir, is_frag) +} + +fn translate_shader( + name: String, + mut state: hir::State, + hir: hir::TranslationUnit, + is_frag: bool, + uniform_indices: &UniformIndices, + include_file: Option, +) -> String { + //println!("{:#?}", state); + + hir::infer_run_class(&mut state, &hir); + + let mut uniforms = Vec::new(); + let mut inputs = Vec::new(); + let mut outputs = Vec::new(); + + for i in &hir { + match i { + hir::ExternalDeclaration::Declaration(hir::Declaration::InitDeclaratorList(ref d)) => { + match &state.sym(d.head.name).decl { + hir::SymDecl::Global(storage, ..) + if state.used_globals.borrow().contains(&d.head.name) => + { + match storage { + hir::StorageClass::Uniform | hir::StorageClass::Sampler(..) => { + uniforms.push(d.head.name); + } + hir::StorageClass::In => { + inputs.push(d.head.name); + } + hir::StorageClass::Out | hir::StorageClass::FragColor(_) => { + outputs.push(d.head.name); + } + _ => {} + } + } + _ => {} + } + } + _ => {} + } + } + + //println!("{:#?}", hir); + + let mut state = OutputState { + hir: state, + output: String::new(), + buffer: RefCell::new(String::new()), + indent: 0, + should_indent: false, + output_cxx: false, + mask: None, + cond_index: 0, + return_type: None, + return_declared: false, + return_vector: false, + is_scalar: Cell::new(false), + is_lval: Cell::new(false), + name: name.clone(), + kind: if is_frag { + ShaderKind::Fragment + } else { + ShaderKind::Vertex + }, + functions: HashMap::new(), + deps: RefCell::new(Vec::new()), + vector_mask: 0, + uses_discard: false, + has_draw_span_rgba8: false, + has_draw_span_r8: false, + used_globals: RefCell::new(Vec::new()), + texel_fetches: RefCell::new(Vec::new()), + }; + + show_translation_unit(&mut state, &hir); + let _output_glsl = state.finish_output(); + + state.should_indent = true; + state.output_cxx = true; + + if state.output_cxx { + let part_name = name.to_owned() + + match state.kind { + ShaderKind::Vertex => "_vert", + ShaderKind::Fragment => "_frag", + }; + let shader_impl = match state.kind { + ShaderKind::Vertex => "VertexShaderImpl", + ShaderKind::Fragment => "FragmentShaderImpl", + }; + + if state.kind == ShaderKind::Vertex { + write!(state, "struct {}_program : ProgramImpl {{\n", name); + write_get_uniform_index(&mut state, uniform_indices); + write_program_samplers(&mut state, uniform_indices); + write_bind_attrib_location(&mut state, &inputs); + write!(state, "VertexShaderImpl* get_vertex_shader() override;\n"); + write!( + state, + "FragmentShaderImpl* get_fragment_shader() override;\n" + ); + write!( + state, + "static ProgramImpl* loader() {{ return new {}_program; }}\n", + name + ); + write!(state, "}};\n\n"); + } + + write!(state, "struct {} : {} {{\n", part_name, shader_impl); + write!(state, "typedef {} Self;\n", part_name); + + show_translation_unit(&mut state, &hir); + + if let Some(include_file) = include_file { + write_include_file(&mut state, include_file); + } + + write_set_uniform_1i(&mut state, &uniforms, uniform_indices); + write_set_uniform_4fv(&mut state, &uniforms, uniform_indices); + write_set_uniform_matrix4fv(&mut state, &uniforms, uniform_indices); + + let pruned_inputs: Vec<_> = inputs + .iter() + .filter(|i| state.used_globals.borrow().contains(i)) + .cloned() + .collect(); + let pruned_uniforms: Vec<_> = uniforms + .iter() + .filter(|u| state.used_globals.borrow().contains(u)) + .cloned() + .collect(); + + if state.kind == ShaderKind::Vertex { + write_load_attribs(&mut state, &pruned_inputs); + write_store_outputs(&mut state, &outputs); + } else { + write_read_inputs(&mut state, &pruned_inputs); + } + write_bind_textures(&mut state, &pruned_uniforms); + + write_abi(&mut state); + write!(state, "}};\n\n"); + + if state.kind == ShaderKind::Vertex { + write!( + state, + "VertexShaderImpl* {}_program::get_vertex_shader() {{ return new {}; }}\n", + name, part_name + ); + } else { + write!( + state, + "FragmentShaderImpl* {}_program::get_fragment_shader() {{ return new {}; }}\n", + name, part_name + ); + } + + define_global_consts(&mut state, &hir, &part_name); + } else { + show_translation_unit(&mut state, &hir); + } + let output_cxx = state.finish_output(); + + //let mut hir = std::fs::File::create("hir").unwrap(); + //hir.write(output_glsl.as_bytes()); + + output_cxx +} + +fn write_get_uniform_index(state: &mut OutputState, uniform_indices: &UniformIndices) { + write!( + state, + "int get_uniform(const char *name) const override {{\n" + ); + for (uniform_name, (index, _, _)) in uniform_indices.iter() { + write!( + state, + " if (strcmp(\"{}\", name) == 0) {{ return {}; }}\n", + uniform_name, index + ); + } + write!(state, " return -1;\n"); + write!(state, "}}\n"); +} + +fn float4_compatible(ty: hir::TypeKind) -> bool { + match ty { + _ => false, + } +} + +fn matrix4_compatible(ty: hir::TypeKind) -> bool { + match ty { + hir::TypeKind::Mat4 => true, + _ => false, + } +} + +fn write_program_samplers(state: &mut OutputState, uniform_indices: &UniformIndices) { + write!(state, "struct Samplers {{\n"); + for (name, (_, tk, storage)) in uniform_indices.iter() { + match tk { + hir::TypeKind::Sampler2D + | hir::TypeKind::ISampler2D + | hir::TypeKind::Sampler2DArray => { + write!(state, " "); + show_type_kind(state, &tk); + let suffix = if let hir::StorageClass::Sampler(format) = storage { + format.type_suffix() + } else { + None + }; + write!(state, "{}_impl {}_impl;\n", suffix.unwrap_or(""), name); + write!(state, " int {}_slot;\n", name); + } + _ => {} + } + } + write!(state, "}} samplers;\n"); + + write!( + state, + "bool set_sampler(int index, int value) override {{\n" + ); + write!(state, " switch (index) {{\n"); + for (name, (index, tk, _)) in uniform_indices.iter() { + match tk { + hir::TypeKind::Sampler2D + | hir::TypeKind::ISampler2D + | hir::TypeKind::Sampler2DArray => { + write!(state, " case {}:\n", index); + write!(state, " samplers.{}_slot = value;\n", name); + write!(state, " return true;\n"); + } + _ => {} + } + } + write!(state, " }}\n"); + write!(state, " return false;\n"); + write!(state, "}}\n"); +} + +fn write_bind_textures(state: &mut OutputState, uniforms: &[hir::SymRef]) { + write!( + state, + "static void bind_textures(Self *self, {}_program *prog) {{\n", + state.name + ); + for i in uniforms { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(hir::StorageClass::Sampler(_format), _, ty, _) => { + let name = sym.name.as_str(); + match ty.kind { + hir::TypeKind::Sampler2D => write!(state, + " self->{0} = lookup_sampler(&prog->samplers.{0}_impl, prog->samplers.{0}_slot);\n", + name), + hir::TypeKind::ISampler2D => write!(state, + " self->{0} = lookup_isampler(&prog->samplers.{0}_impl, prog->samplers.{0}_slot);\n", + name), + hir::TypeKind::Sampler2DArray => write!(state, + " self->{0} = lookup_sampler_array(&prog->samplers.{0}_impl, prog->samplers.{0}_slot);\n", + name), + _ => {} + }; + } + hir::SymDecl::Global(..) => {} + _ => panic!(), + } + } + write!(state, "}}\n"); +} + +fn write_set_uniform_1i( + state: &mut OutputState, + uniforms: &[hir::SymRef], + uniform_indices: &UniformIndices, +) { + write!( + state, + "static void set_uniform_1i(Self *self, int index, int value) {{\n" + ); + write!(state, " switch (index) {{\n"); + for i in uniforms { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(_, _, ty, _) => { + let name = sym.name.as_str(); + let (index, _, _) = uniform_indices.get(name).unwrap(); + write!(state, " case {}:\n", index); + match ty.kind { + hir::TypeKind::Int => write!( + state, + " self->{} = {}(value);\n", + name, + scalar_type_name(state, ty) + ), + _ => write!(state, " assert(0); // {}\n", name), + }; + write!(state, " break;\n"); + } + _ => panic!(), + } + } + write!(state, " }}\n"); + write!(state, "}}\n"); +} + +fn write_set_uniform_4fv( + state: &mut OutputState, + uniforms: &[hir::SymRef], + uniform_indices: &UniformIndices, +) { + write!( + state, + "static void set_uniform_4fv(Self *self, int index, const float *value) {{\n" + ); + write!(state, " switch (index) {{\n"); + for i in uniforms { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(_, _, ty, _) => { + let name = sym.name.as_str(); + let (index, _, _) = uniform_indices.get(name).unwrap(); + write!(state, " case {}:\n", index); + if float4_compatible(ty.kind.clone()) { + write!( + state, + " self->{} = {}(value);\n", + name, + scalar_type_name(state, ty) + ); + } else { + write!(state, " assert(0); // {}\n", name); + } + write!(state, " break;\n"); + } + _ => panic!(), + } + } + write!(state, " }}\n"); + write!(state, "}}\n"); +} + +fn write_set_uniform_matrix4fv( + state: &mut OutputState, + uniforms: &[hir::SymRef], + uniform_indices: &UniformIndices, +) { + write!( + state, + "static void set_uniform_matrix4fv(Self *self, int index, const float *value) {{\n" + ); + write!(state, " switch (index) {{\n"); + for i in uniforms { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(_, _, ty, _) => { + let name = sym.name.as_str(); + let (index, _, _) = uniform_indices.get(name).unwrap(); + + write!(state, " case {}:\n", index); + if matrix4_compatible(ty.kind.clone()) { + write!( + state, + " self->{} = mat4_scalar::load_from_ptr(value);\n", + name + ); + } else { + write!(state, " assert(0); // {}\n", name); + } + write!(state, " break;\n"); + } + _ => panic!(), + } + } + write!(state, " }}\n"); + write!(state, "}}\n"); +} + +fn write_bind_attrib_location(state: &mut OutputState, attribs: &[hir::SymRef]) { + write!(state, "struct AttribLocations {{\n"); + for i in attribs { + let sym = state.hir.sym(*i); + write!(state, " int {} = NULL_ATTRIB;\n", sym.name.as_str()); + } + write!(state, "}} attrib_locations;\n"); + + write!( + state, + "void bind_attrib(const char* name, int index) override {{\n" + ); + for i in attribs { + let sym = state.hir.sym(*i); + write!( + state, + " if (strcmp(\"{0}\", name) == 0) {{ attrib_locations.{0} = index; return; }}\n", + sym.name.as_str() + ); + } + write!(state, "}}\n"); + + write!( + state, + "int get_attrib(const char* name) const override {{\n" + ); + for i in attribs { + let sym = state.hir.sym(*i); + write!(state, + " if (strcmp(\"{0}\", name) == 0) {{\ + return attrib_locations.{0} != NULL_ATTRIB ? attrib_locations.{0} : -1;\ + }}\n", + sym.name.as_str()); + } + write!(state, " return -1;\n"); + write!(state, "}}\n"); +} + +fn scalar_type_name(state: &OutputState, ty: &Type) -> String { + let kind_name = if let Some(name) = ty.kind.cxx_primitive_scalar_type_name() { + name.into() + } else { + let buffer = state.push_buffer(); + show_type(state, ty); + state.pop_buffer(buffer) + "_scalar" + }; + if let Some(ref array) = ty.array_sizes { + let size = match &array.sizes[..] { + [size] => size, + _ => panic!(), + }; + let buffer = state.push_buffer(); + show_hir_expr(state, size); + let size_string = state.pop_buffer(buffer); + + format!("Array<{}, {}>", kind_name, size_string) + } else { + kind_name + } +} + +//fn type_name(state: &OutputState, ty: &Type) -> String { +// let buffer = state.push_buffer(); +// show_type(state, ty); +// state.pop_buffer(buffer) +//} + +fn write_load_attribs(state: &mut OutputState, attribs: &[hir::SymRef]) { + write!(state, "static void load_attribs(\ + Self *self, {}_program *prog, VertexAttrib *attribs, unsigned short *indices, \ + int start, int instance, int count) {{\n", state.name); + for i in attribs { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(_, _interpolation, _ty, run_class) => { + let name = sym.name.as_str(); + let func = if *run_class == hir::RunClass::Scalar { + "load_flat_attrib" + } else { + "load_attrib" + }; + write!(state, + " {0}(self->{1}, attribs[prog->attrib_locations.{1}], indices, start, instance, count);\n", + func, name); + } + _ => panic!(), + } + } + write!(state, "}}\n"); +} + +fn write_store_outputs(state: &mut OutputState, outputs: &[hir::SymRef]) { + let is_scalar = state.is_scalar.replace(true); + write!(state, "struct FlatOutputs {{\n"); + for i in outputs { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(_, _, ty, run_class) => { + if *run_class == hir::RunClass::Scalar { + show_type(state, ty); + write!(state, " {};\n", sym.name.as_str()); + } + } + _ => panic!(), + } + } + write!(state, "}};\n"); + + write!(state, "struct InterpOutputs {{\n"); + for i in outputs { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(_, _, ty, run_class) => { + if *run_class != hir::RunClass::Scalar { + show_type(state, ty); + write!(state, " {};\n", sym.name.as_str()); + } + } + _ => panic!(), + } + } + + write!(state, "}};\n"); + state.is_scalar.set(is_scalar); + + write!( + state, + "ALWAYS_INLINE void store_flat_outputs(char* dest_ptr) {{\n" + ); + write!( + state, + " auto* dest = reinterpret_cast(dest_ptr);\n" + ); + for i in outputs { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(_, _, _, run_class) => { + if *run_class == hir::RunClass::Scalar { + let name = sym.name.as_str(); + write!(state, " dest->{} = {};\n", name, name); + } + } + _ => panic!(), + } + } + write!(state, "}}\n"); + + write!( + state, + "ALWAYS_INLINE void store_interp_outputs(char* dest_ptr, size_t stride) {{\n" + ); + write!(state, " for(int n = 0; n < 4; n++) {{\n"); + write!( + state, + " auto* dest = reinterpret_cast(dest_ptr);\n" + ); + for i in outputs { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(_, _, _, run_class) => { + if *run_class != hir::RunClass::Scalar { + let name = sym.name.as_str(); + write!(state, " dest->{} = get_nth({}, n);\n", name, name); + } + } + _ => panic!(), + } + } + write!(state, " dest_ptr += stride;\n"); + write!(state, " }}\n"); + write!(state, "}}\n"); +} + +fn write_read_inputs(state: &mut OutputState, inputs: &[hir::SymRef]) { + write!( + state, + "typedef {}_vert::FlatOutputs FlatInputs;\n", + state.name + ); + write!( + state, + "typedef {}_vert::InterpOutputs InterpInputs;\n", + state.name + ); + + write!( + state, + "static void read_flat_inputs(Self *self, const FlatInputs *src) {{\n" + ); + for i in inputs { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(_, _, _, run_class) => { + if *run_class == hir::RunClass::Scalar { + let name = sym.name.as_str(); + write!(state, " self->{} = src->{};\n", name, name); + } + } + _ => panic!(), + } + } + write!(state, "}}\n"); + + write!(state, "InterpInputs interp_step;\n"); + write!(state, + "static void read_interp_inputs(\ + Self *self, const InterpInputs *init, const InterpInputs *step, float step_width) {{\n"); + for i in inputs { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(_, _, _, run_class) => { + if *run_class != hir::RunClass::Scalar { + let name = sym.name.as_str(); + write!( + state, + " self->{0} = init_interp(init->{0}, step->{0});\n", + name + ); + write!( + state, + " self->interp_step.{0} = step->{0} * step_width;\n", + name + ); + } + } + _ => panic!(), + } + } + write!(state, "}}\n"); + + write!(state, "ALWAYS_INLINE void step_interp_inputs() {{\n"); + if (state.hir.used_fragcoord & 1) != 0 { + write!(state, " step_fragcoord();\n"); + } + for i in inputs { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(_, _, _, run_class) => { + if *run_class != hir::RunClass::Scalar { + let name = sym.name.as_str(); + write!(state, " {} += interp_step.{};\n", name, name); + } + } + _ => panic!(), + } + } + write!(state, "}}\n"); + + if state.has_draw_span_rgba8 || state.has_draw_span_r8 { + write!( + state, + "ALWAYS_INLINE void step_interp_inputs(int chunks) {{\n" + ); + if (state.hir.used_fragcoord & 1) != 0 { + write!(state, " step_fragcoord(chunks);\n"); + } + for i in inputs { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(_, _, _, run_class) => { + if *run_class != hir::RunClass::Scalar { + let name = sym.name.as_str(); + write!(state, " {} += interp_step.{} * chunks;\n", name, name); + } + } + _ => panic!(), + } + } + write!(state, "}}\n"); + } +} + +fn write_include_file(state: &mut OutputState, include_file: String) { + let include_contents = std::fs::read_to_string(&include_file).unwrap(); + + let mut offset = 0; + while offset < include_contents.len() { + let s = &include_contents[offset ..]; + if let Some(start_proto) = s.find("draw_span") { + let s = &s[start_proto ..]; + if let Some(end_proto) = s.find(')') { + let proto = &s[.. end_proto]; + if proto.contains("uint32_t") { + state.has_draw_span_rgba8 = true; + } else if proto.contains("uint8_t") { + state.has_draw_span_r8 = true; + } + offset += start_proto + end_proto; + continue; + } + } + break; + } + + let include_name = std::path::Path::new(&include_file) + .file_name() + .unwrap() + .to_string_lossy(); + write!(state, "\n#include \"{}\"\n\n", include_name); +} + +pub struct OutputState { + hir: hir::State, + output: String, + buffer: RefCell, + should_indent: bool, + output_cxx: bool, + indent: i32, + mask: Option>, + cond_index: usize, + return_type: Option>, + return_declared: bool, + return_vector: bool, + is_scalar: Cell, + is_lval: Cell, + name: String, + kind: ShaderKind, + functions: HashMap<(hir::SymRef, u32), bool>, + deps: RefCell>, + vector_mask: u32, + uses_discard: bool, + has_draw_span_rgba8: bool, + has_draw_span_r8: bool, + used_globals: RefCell>, + texel_fetches: RefCell>, +} + +use std::fmt::{Arguments, Write}; + +impl OutputState { + fn indent(&mut self) { + if self.should_indent { + self.indent += 1 + } + } + fn outdent(&mut self) { + if self.should_indent { + self.indent -= 1 + } + } + + fn write(&self, s: &str) { + self.buffer.borrow_mut().push_str(s); + } + + fn flush_buffer(&mut self) { + self.output.push_str(&self.buffer.borrow()); + self.buffer.borrow_mut().clear(); + } + + fn finish_output(&mut self) -> String { + self.flush_buffer(); + + let mut s = String::new(); + mem::swap(&mut self.output, &mut s); + s + } + + fn push_buffer(&self) -> String { + self.buffer.replace(String::new()) + } + + fn pop_buffer(&self, s: String) -> String { + self.buffer.replace(s) + } + + fn write_fmt(&self, args: Arguments) { + let _ = self.buffer.borrow_mut().write_fmt(args); + } +} + +pub fn show_identifier(state: &OutputState, i: &syntax::Identifier) { + state.write(&i.0); +} + +fn glsl_primitive_type_name_to_cxx(glsl_name: &str) -> &str { + hir::TypeKind::from_glsl_primitive_type_name(glsl_name) + .and_then(|kind| kind.cxx_primitive_type_name()) + .unwrap_or(glsl_name) +} + +fn add_used_global(state: &OutputState, i: &hir::SymRef) { + let mut globals = state.used_globals.borrow_mut(); + if !globals.contains(i) { + globals.push(*i); + } +} + +pub fn show_sym(state: &OutputState, i: &hir::SymRef) { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::NativeFunction(_, ref cxx_name) => { + let mut name = sym.name.as_str(); + if state.output_cxx { + name = cxx_name.unwrap_or(name); + } + state.write(name); + } + hir::SymDecl::Global(..) => { + if state.output_cxx { + add_used_global(state, i); + } + let mut name = sym.name.as_str(); + if state.output_cxx { + name = glsl_primitive_type_name_to_cxx(name); + } + state.write(name); + } + hir::SymDecl::UserFunction(..) | hir::SymDecl::Local(..) | hir::SymDecl::Struct(..) => { + let mut name = sym.name.as_str(); + // we want to replace constructor names + if state.output_cxx { + name = glsl_primitive_type_name_to_cxx(name); + } + state.write(name); + } + } +} + +pub fn show_variable(state: &OutputState, i: &hir::SymRef) { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(_, _, ty, _) => { + show_type(state, ty); + state.write(" "); + let mut name = sym.name.as_str(); + if state.output_cxx { + name = glsl_primitive_type_name_to_cxx(name); + } + state.write(name); + } + _ => panic!(), + } +} + +pub fn write_default_constructor(state: &OutputState, name: &str) { + // write default constructor + let _ = write!(state, "{}() = default;\n", name); +} + +pub fn write_constructor(state: &OutputState, name: &str, s: &hir::StructFields) { + if s.fields.len() == 1 { + state.write("explicit "); + } + let _ = write!(state, "{}(", name); + let mut first_field = true; + for field in &s.fields { + if !first_field { + state.write(", "); + } + show_type(state, &field.ty); + state.write(" "); + show_identifier_and_type(state, &field.name, &field.ty); + first_field = false; + } + state.write(") : "); + + let mut first_field = true; + for field in &s.fields { + if !first_field { + state.write(", "); + } + let _ = write!(state, "{}({})", field.name, field.name); + first_field = false; + } + state.write("{}\n"); +} + +pub fn write_convert_constructor(state: &OutputState, name: &str, s: &hir::StructFields) { + if s.fields.len() == 1 { + state.write("explicit "); + } + let _ = write!(state, "{}(", name); + let mut first_field = true; + for field in &s.fields { + if !first_field { + state.write(", "); + } + + let is_scalar = state.is_scalar.replace(true); + show_type(state, &field.ty); + state.is_scalar.set(is_scalar); + + state.write(" "); + + show_identifier_and_type(state, &field.name, &field.ty); + first_field = false; + } + state.write(")"); + + let mut first_field = true; + for hir::StructField { ty, name } in &s.fields { + if ty.array_sizes.is_none() { + if first_field { + state.write(":"); + } else { + state.write(","); + } + let _ = write!(state, "{}({})", name, name); + first_field = false; + } + } + state.write("{\n"); + for hir::StructField { ty, name } in &s.fields { + if ty.array_sizes.is_some() { + let _ = write!(state, "this->{}.convert({});\n", name, name); + } + } + state.write("}\n"); + + let _ = write!(state, "IMPLICIT {}({}_scalar s)", name, name); + let mut first_field = true; + for hir::StructField { ty, name } in &s.fields { + if ty.array_sizes.is_none() { + if first_field { + state.write(":"); + } else { + state.write(","); + } + let _ = write!(state, "{}(s.{})", name, name); + first_field = false; + } + } + state.write("{\n"); + for hir::StructField { ty, name } in &s.fields { + if ty.array_sizes.is_some() { + let _ = write!(state, "{}.convert(s.{});\n", name, name); + } + } + state.write("}\n"); +} + +pub fn write_if_then_else(state: &OutputState, name: &str, s: &hir::StructFields) { + let _ = write!( + state, + "friend {} if_then_else(I32 c, {} t, {} e) {{ return {}(\n", + name, name, name, name + ); + let mut first_field = true; + for field in &s.fields { + if !first_field { + state.write(", "); + } + let _ = write!(state, "if_then_else(c, t.{}, e.{})", field.name, field.name); + first_field = false; + } + state.write(");\n}"); +} + +pub fn show_storage_class(state: &OutputState, q: &hir::StorageClass) { + match *q { + hir::StorageClass::None => {} + hir::StorageClass::Const => { + state.write("const "); + } + hir::StorageClass::In => { + state.write("in "); + } + hir::StorageClass::Out => { + state.write("out "); + } + hir::StorageClass::FragColor(index) => { + write!(state, "layout(location = 0, index = {}) out ", index); + } + hir::StorageClass::Uniform | hir::StorageClass::Sampler(..) => { + state.write("uniform "); + } + } +} + +pub fn show_sym_decl(state: &OutputState, i: &hir::SymRef) { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(storage, ..) => { + if !state.output_cxx { + show_storage_class(state, storage) + } + if storage == &hir::StorageClass::Const { + state.write("static constexpr "); + } + let mut name = sym.name.as_str(); + if state.output_cxx { + name = glsl_primitive_type_name_to_cxx(name); + } + state.write(name); + } + hir::SymDecl::Local(storage, ..) => { + if !state.output_cxx { + show_storage_class(state, storage) + } + if storage == &hir::StorageClass::Const { + state.write("const "); + } + let mut name = sym.name.as_str(); + if state.output_cxx { + name = glsl_primitive_type_name_to_cxx(name); + } + state.write(name); + } + hir::SymDecl::Struct(s) => { + let name = sym.name.as_str(); + + if state.output_cxx { + let name_scalar = format!("{}_scalar", name); + write!(state, "struct {} {{\n", name_scalar); + let is_scalar = state.is_scalar.replace(true); + for field in &s.fields { + show_struct_field(state, field); + } + write_default_constructor(state, &name_scalar); + write_constructor(state, &name_scalar, s); + state.is_scalar.set(is_scalar); + state.write("};\n"); + } + + write!(state, "struct {} {{\n", name); + for field in &s.fields { + show_struct_field(state, field); + } + + // write if_then_else + if state.output_cxx { + write_default_constructor(state, name); + write_constructor(state, name, s); + write_convert_constructor(state, name, s); + write_if_then_else(state, name, s); + } + state.write("}"); + } + _ => panic!(), + } +} + +pub fn show_type_name(state: &OutputState, t: &syntax::TypeName) { + state.write(&t.0); +} + +pub fn show_type_specifier_non_array(state: &mut OutputState, t: &syntax::TypeSpecifierNonArray) { + if let Some(kind) = hir::TypeKind::from_primitive_type_specifier(t) { + show_type_kind(state, &kind); + } else { + match t { + syntax::TypeSpecifierNonArray::Struct(ref _s) => panic!(), //show_struct_non_declaration(state, s), + syntax::TypeSpecifierNonArray::TypeName(ref tn) => show_type_name(state, tn), + _ => unreachable!(), + } + } +} + +pub fn show_type_kind(state: &OutputState, t: &hir::TypeKind) { + if state.output_cxx { + if state.is_scalar.get() { + if let Some(name) = t.cxx_primitive_scalar_type_name() { + state.write(name); + } else if let Some(name) = t.cxx_primitive_type_name() { + let mut scalar_name = String::from(name); + scalar_name.push_str("_scalar"); + state.write(scalar_name.as_str()); + } else { + match t { + hir::TypeKind::Struct(ref s) => { + let mut scalar_name = String::from(state.hir.sym(*s).name.as_str()); + scalar_name.push_str("_scalar"); + state.write(scalar_name.as_str()); + } + _ => unreachable!(), + } + } + } else if let Some(name) = t.cxx_primitive_type_name() { + state.write(name); + } else { + match t { + hir::TypeKind::Struct(ref s) => { + state.write(state.hir.sym(*s).name.as_str()); + } + _ => unreachable!(), + } + } + } else if let Some(name) = t.glsl_primitive_type_name() { + state.write(name); + } else { + match t { + hir::TypeKind::Struct(ref s) => { + state.write(state.hir.sym(*s).name.as_str()); + } + _ => unreachable!(), + } + } +} + +pub fn show_type_specifier(state: &mut OutputState, t: &syntax::TypeSpecifier) { + show_type_specifier_non_array(state, &t.ty); + + if let Some(ref arr_spec) = t.array_specifier { + show_array_spec(state, arr_spec); + } +} + +pub fn show_type(state: &OutputState, t: &Type) { + if !state.output_cxx { + if let Some(ref precision) = t.precision { + show_precision_qualifier(state, precision); + state.write(" "); + } + } + + if state.output_cxx { + if let Some(ref array) = t.array_sizes { + state.write("Array<"); + show_type_kind(state, &t.kind); + let size = match &array.sizes[..] { + [size] => size, + _ => panic!(), + }; + state.write(","); + show_hir_expr(state, size); + state.write(">"); + } else { + show_type_kind(state, &t.kind); + } + } else { + show_type_kind(state, &t.kind); + } + + /*if let Some(ref arr_spec) = t.array_sizes { + panic!(); + }*/ +} + +/*pub fn show_fully_specified_type(state: &mut OutputState, t: &FullySpecifiedType) { + state.flat = false; + if let Some(ref qual) = t.qualifier { + if !state.output_cxx { + show_type_qualifier(state, &qual); + } else { + state.flat = + qual.qualifiers.0.iter() + .flat_map(|q| match q { syntax::TypeQualifierSpec::Interpolation(Flat) => Some(()), _ => None}) + .next().is_some(); + } + state.write(" "); + } + + show_type_specifier(state, &t.ty); +}*/ + +/*pub fn show_struct_non_declaration(state: &mut OutputState, s: &syntax::StructSpecifier) { + state.write("struct "); + + if let Some(ref name) = s.name { + let _ = write!(state, "{} ", name); + } + + state.write("{\n"); + + for field in &s.fields.0 { + show_struct_field(state, field); + } + + state.write("}"); +}*/ + +pub fn show_struct(_state: &OutputState, _s: &syntax::StructSpecifier) { + panic!(); + //show_struct_non_declaration(state, s); + //state.write(";\n"); +} + +pub fn show_struct_field(state: &OutputState, field: &hir::StructField) { + show_type(state, &field.ty); + state.write(" "); + + show_identifier_and_type(state, &field.name, &field.ty); + + state.write(";\n"); +} + +pub fn show_array_spec(state: &OutputState, a: &syntax::ArraySpecifier) { + match *a { + syntax::ArraySpecifier::Unsized => { + state.write("[]"); + } + syntax::ArraySpecifier::ExplicitlySized(ref e) => { + state.write("["); + show_expr(state, &e); + state.write("]"); + } + } +} + +pub fn show_identifier_and_type(state: &OutputState, ident: &syntax::Identifier, ty: &hir::Type) { + let _ = write!(state, "{}", ident); + + if !state.output_cxx { + if let Some(ref arr_spec) = ty.array_sizes { + show_array_sizes(state, &arr_spec); + } + } +} + +pub fn show_arrayed_identifier(state: &OutputState, ident: &syntax::ArrayedIdentifier) { + let _ = write!(state, "{}", ident.ident); + + if let Some(ref arr_spec) = ident.array_spec { + show_array_spec(state, &arr_spec); + } +} + +pub fn show_array_sizes(state: &OutputState, a: &hir::ArraySizes) { + state.write("["); + match &a.sizes[..] { + [a] => show_hir_expr(state, a), + _ => panic!(), + } + + state.write("]"); + /* + match *a { + syntax::ArraySpecifier::Unsized => { state.write("[]"); } + syntax::ArraySpecifier::ExplicitlySized(ref e) => { + state.write("["); + show_expr(state, &e); + state.write("]"); + } + }*/ +} + +pub fn show_type_qualifier(state: &OutputState, q: &hir::TypeQualifier) { + let mut qualifiers = q.qualifiers.0.iter(); + let first = qualifiers.next().unwrap(); + + show_type_qualifier_spec(state, first); + + for qual_spec in qualifiers { + state.write(" "); + show_type_qualifier_spec(state, qual_spec) + } +} + +pub fn show_type_qualifier_spec(state: &OutputState, q: &hir::TypeQualifierSpec) { + match *q { + hir::TypeQualifierSpec::Layout(ref l) => show_layout_qualifier(state, &l), + hir::TypeQualifierSpec::Parameter(ref _p) => panic!(), + hir::TypeQualifierSpec::Memory(ref _m) => panic!(), + hir::TypeQualifierSpec::Invariant => { + state.write("invariant"); + } + hir::TypeQualifierSpec::Precise => { + state.write("precise"); + } + } +} + +pub fn show_syntax_storage_qualifier(state: &OutputState, q: &syntax::StorageQualifier) { + match *q { + syntax::StorageQualifier::Const => { + state.write("const"); + } + syntax::StorageQualifier::InOut => { + state.write("inout"); + } + syntax::StorageQualifier::In => { + state.write("in"); + } + syntax::StorageQualifier::Out => { + state.write("out"); + } + syntax::StorageQualifier::Centroid => { + state.write("centroid"); + } + syntax::StorageQualifier::Patch => { + state.write("patch"); + } + syntax::StorageQualifier::Sample => { + state.write("sample"); + } + syntax::StorageQualifier::Uniform => { + state.write("uniform"); + } + syntax::StorageQualifier::Attribute => { + state.write("attribute"); + } + syntax::StorageQualifier::Varying => { + state.write("varying"); + } + syntax::StorageQualifier::Buffer => { + state.write("buffer"); + } + syntax::StorageQualifier::Shared => { + state.write("shared"); + } + syntax::StorageQualifier::Coherent => { + state.write("coherent"); + } + syntax::StorageQualifier::Volatile => { + state.write("volatile"); + } + syntax::StorageQualifier::Restrict => { + state.write("restrict"); + } + syntax::StorageQualifier::ReadOnly => { + state.write("readonly"); + } + syntax::StorageQualifier::WriteOnly => { + state.write("writeonly"); + } + syntax::StorageQualifier::Subroutine(ref n) => show_subroutine(state, &n), + } +} + +pub fn show_subroutine(state: &OutputState, types: &[syntax::TypeName]) { + state.write("subroutine"); + + if !types.is_empty() { + state.write("("); + + let mut types_iter = types.iter(); + let first = types_iter.next().unwrap(); + + show_type_name(state, first); + + for type_name in types_iter { + state.write(", "); + show_type_name(state, type_name); + } + + state.write(")"); + } +} + +pub fn show_layout_qualifier(state: &OutputState, l: &syntax::LayoutQualifier) { + let mut qualifiers = l.ids.0.iter(); + let first = qualifiers.next().unwrap(); + + state.write("layout ("); + show_layout_qualifier_spec(state, first); + + for qual_spec in qualifiers { + state.write(", "); + show_layout_qualifier_spec(state, qual_spec); + } + + state.write(")"); +} + +pub fn show_layout_qualifier_spec(state: &OutputState, l: &syntax::LayoutQualifierSpec) { + match *l { + syntax::LayoutQualifierSpec::Identifier(ref i, Some(ref e)) => { + let _ = write!(state, "{} = ", i); + show_expr(state, &e); + } + syntax::LayoutQualifierSpec::Identifier(ref i, None) => show_identifier(state, &i), + syntax::LayoutQualifierSpec::Shared => { + state.write("shared"); + } + } +} + +pub fn show_precision_qualifier(state: &OutputState, p: &syntax::PrecisionQualifier) { + match *p { + syntax::PrecisionQualifier::High => { + state.write("highp"); + } + syntax::PrecisionQualifier::Medium => { + state.write("mediump"); + } + syntax::PrecisionQualifier::Low => { + state.write("low"); + } + } +} + +pub fn show_interpolation_qualifier(state: &OutputState, i: &syntax::InterpolationQualifier) { + match *i { + syntax::InterpolationQualifier::Smooth => { + state.write("smooth"); + } + syntax::InterpolationQualifier::Flat => { + state.write("flat"); + } + syntax::InterpolationQualifier::NoPerspective => { + state.write("noperspective"); + } + } +} + +pub fn show_parameter_qualifier(state: &mut OutputState, i: &Option) { + if let Some(i) = i { + if state.output_cxx { + match *i { + hir::ParameterQualifier::Out => { + state.write("&"); + } + hir::ParameterQualifier::InOut => { + state.write("&"); + } + _ => {} + } + } else { + match *i { + hir::ParameterQualifier::Const => { + state.write("const"); + } + hir::ParameterQualifier::In => { + state.write("in"); + } + hir::ParameterQualifier::Out => { + state.write("out"); + } + hir::ParameterQualifier::InOut => { + state.write("inout"); + } + } + } + } +} + +pub fn show_float(state: &OutputState, x: f32) { + if x.fract() == 0. { + write!(state, "{}.f", x); + } else { + write!(state, "{}f", x); + } +} + +pub fn show_double(state: &OutputState, x: f64) { + // force doubles to print as floats + if x.fract() == 0. { + write!(state, "{}.f", x); + } else { + write!(state, "{}f", x); + } +} + +trait SwizzelSelectorExt { + fn to_args(&self) -> String; +} + +impl SwizzelSelectorExt for SwizzleSelector { + fn to_args(&self) -> String { + let mut s = Vec::new(); + let fs = match self.field_set { + hir::FieldSet::Rgba => ["R", "G", "B", "A"], + hir::FieldSet::Xyzw => ["X", "Y", "Z", "W"], + hir::FieldSet::Stpq => ["S", "T", "P", "Q"], + }; + for i in &self.components { + s.push(fs[*i as usize]) + } + s.join(", ") + } +} + +fn expr_run_class(state: &OutputState, expr: &hir::Expr) -> hir::RunClass { + match &expr.kind { + hir::ExprKind::Variable(i) => symbol_run_class(&state.hir.sym(*i).decl, state.vector_mask), + hir::ExprKind::IntConst(_) + | hir::ExprKind::UIntConst(_) + | hir::ExprKind::BoolConst(_) + | hir::ExprKind::FloatConst(_) + | hir::ExprKind::DoubleConst(_) => hir::RunClass::Scalar, + hir::ExprKind::Unary(_, ref e) => expr_run_class(state, e), + hir::ExprKind::Binary(_, ref l, ref r) => { + expr_run_class(state, l).merge(expr_run_class(state, r)) + } + hir::ExprKind::Ternary(ref c, ref s, ref e) => expr_run_class(state, c) + .merge(expr_run_class(state, s)) + .merge(expr_run_class(state, e)), + hir::ExprKind::Assignment(ref v, _, ref e) => { + expr_run_class(state, v).merge(expr_run_class(state, e)) + } + hir::ExprKind::Bracket(ref e, ref indx) => { + expr_run_class(state, e).merge(expr_run_class(state, indx)) + } + hir::ExprKind::FunCall(ref fun, ref args) => { + let arg_mask: u32 = args.iter().enumerate().fold(0, |mask, (idx, e)| { + if expr_run_class(state, e) == hir::RunClass::Vector { + mask | (1 << idx) + } else { + mask + } + }); + match fun { + hir::FunIdentifier::Identifier(ref sym) => match &state.hir.sym(*sym).decl { + hir::SymDecl::NativeFunction(..) => { + if arg_mask != 0 { + hir::RunClass::Vector + } else { + hir::RunClass::Scalar + } + } + hir::SymDecl::UserFunction(ref fd, ref run_class) => { + let param_mask: u32 = fd.prototype.parameters.iter().enumerate().fold( + arg_mask, + |mask, (idx, param)| { + if let hir::FunctionParameterDeclaration::Named(Some(qual), p) = + param + { + match qual { + hir::ParameterQualifier::InOut + | hir::ParameterQualifier::Out => { + if symbol_run_class( + &state.hir.sym(p.sym).decl, + arg_mask, + ) == hir::RunClass::Vector + { + mask | (1 << idx) + } else { + mask + } + } + _ => mask, + } + } else { + mask + } + }, + ); + match *run_class { + hir::RunClass::Scalar => hir::RunClass::Scalar, + hir::RunClass::Dependent(mask) => { + if (mask & param_mask) != 0 { + hir::RunClass::Vector + } else { + hir::RunClass::Scalar + } + } + _ => hir::RunClass::Vector, + } + } + hir::SymDecl::Struct(..) => { + if arg_mask != 0 { + hir::RunClass::Vector + } else { + hir::RunClass::Scalar + } + } + _ => panic!(), + }, + hir::FunIdentifier::Constructor(..) => { + if arg_mask != 0 { + hir::RunClass::Vector + } else { + hir::RunClass::Scalar + } + } + } + } + hir::ExprKind::Dot(ref e, _) => expr_run_class(state, e), + hir::ExprKind::SwizzleSelector(ref e, _) => expr_run_class(state, e), + hir::ExprKind::PostInc(ref e) => expr_run_class(state, e), + hir::ExprKind::PostDec(ref e) => expr_run_class(state, e), + hir::ExprKind::Comma(_, ref e) => expr_run_class(state, e), + hir::ExprKind::Cond(_, ref e) => expr_run_class(state, e), + hir::ExprKind::CondMask => hir::RunClass::Vector, + } +} + +pub fn show_hir_expr(state: &OutputState, expr: &hir::Expr) { + show_hir_expr_inner(state, expr, false); +} + +pub fn show_hir_expr_inner(state: &OutputState, expr: &hir::Expr, top_level: bool) { + match expr.kind { + hir::ExprKind::Variable(ref i) => show_sym(state, i), + hir::ExprKind::IntConst(ref x) => { + let _ = write!(state, "{}", x); + } + hir::ExprKind::UIntConst(ref x) => { + let _ = write!(state, "{}u", x); + } + hir::ExprKind::BoolConst(ref x) => { + let _ = write!(state, "{}", x); + } + hir::ExprKind::FloatConst(ref x) => show_float(state, *x), + hir::ExprKind::DoubleConst(ref x) => show_double(state, *x), + hir::ExprKind::Unary(ref op, ref e) => { + show_unary_op(state, &op); + state.write("("); + show_hir_expr(state, &e); + state.write(")"); + } + hir::ExprKind::Binary(ref op, ref l, ref r) => { + state.write("("); + show_hir_expr(state, &l); + state.write(")"); + show_binary_op(state, &op); + state.write("("); + show_hir_expr(state, &r); + state.write(")"); + } + hir::ExprKind::Ternary(ref c, ref s, ref e) => { + if state.output_cxx && expr_run_class(state, c) != hir::RunClass::Scalar { + state.write("if_then_else("); + show_hir_expr(state, &c); + state.write(", "); + show_hir_expr(state, &s); + state.write(", "); + show_hir_expr(state, &e); + state.write(")"); + } else { + show_hir_expr(state, &c); + state.write(" ? "); + show_hir_expr(state, &s); + state.write(" : "); + show_hir_expr(state, &e); + } + } + hir::ExprKind::Assignment(ref v, ref op, ref e) => { + let is_output = hir::is_output(v, &state.hir).is_some(); + let is_scalar_var = expr_run_class(state, v) == hir::RunClass::Scalar; + let is_scalar_expr = expr_run_class(state, e) == hir::RunClass::Scalar; + let force_scalar = is_scalar_var && !is_scalar_expr; + + if let Some(mask) = &state.mask { + let is_scalar_mask = expr_run_class(state, mask) == hir::RunClass::Scalar; + let force_scalar_mask = is_scalar_var && is_scalar_expr && !is_scalar_mask; + + if force_scalar || force_scalar_mask { + if top_level { + state.write("if ("); + } else { + state.write("("); + } + } else { + state.is_lval.set(true); + show_hir_expr(state, &v); + state.is_lval.set(false); + state.write(" = if_then_else("); + } + + if is_output && state.return_declared { + state.write("(("); + show_hir_expr(state, mask); + state.write(")&ret_mask)"); + } else { + show_hir_expr(state, mask); + } + if force_scalar || force_scalar_mask { + if top_level { + state.write("[0]) { "); + } else { + state.write("[0] ? "); + } + state.is_lval.set(true); + show_hir_expr(state, &v); + state.is_lval.set(false); + state.write(" = "); + } else { + state.write(","); + } + + if op != &syntax::AssignmentOp::Equal { + show_hir_expr(state, &v); + } + + match *op { + syntax::AssignmentOp::Equal => {} + syntax::AssignmentOp::Mult => { + state.write("*"); + } + syntax::AssignmentOp::Div => { + state.write("/"); + } + syntax::AssignmentOp::Mod => { + state.write("%"); + } + syntax::AssignmentOp::Add => { + state.write("+"); + } + syntax::AssignmentOp::Sub => { + state.write("-"); + } + syntax::AssignmentOp::LShift => { + state.write("<<"); + } + syntax::AssignmentOp::RShift => { + state.write(">>"); + } + syntax::AssignmentOp::And => { + state.write("&"); + } + syntax::AssignmentOp::Xor => { + state.write("^"); + } + syntax::AssignmentOp::Or => { + state.write("|"); + } + } + if force_scalar { + state.write("force_scalar("); + } + show_hir_expr(state, &e); + if force_scalar { + state.write(")"); + } + if force_scalar || force_scalar_mask { + if top_level { + state.write("; }"); + } else { + state.write(" : "); + show_hir_expr(state, &v); + state.write(")"); + } + } else { + state.write(","); + show_hir_expr(state, &v); + state.write(")"); + } + } else { + state.is_lval.set(true); + show_hir_expr(state, &v); + state.is_lval.set(false); + state.write(" "); + + if is_output && state.return_declared { + state.write("= "); + if force_scalar { + state.write("force_scalar("); + } + state.write("if_then_else(ret_mask,"); + + if op != &syntax::AssignmentOp::Equal { + show_hir_expr(state, &v); + } + + match *op { + syntax::AssignmentOp::Equal => {} + syntax::AssignmentOp::Mult => { + state.write("*"); + } + syntax::AssignmentOp::Div => { + state.write("/"); + } + syntax::AssignmentOp::Mod => { + state.write("%"); + } + syntax::AssignmentOp::Add => { + state.write("+"); + } + syntax::AssignmentOp::Sub => { + state.write("-"); + } + syntax::AssignmentOp::LShift => { + state.write("<<"); + } + syntax::AssignmentOp::RShift => { + state.write(">>"); + } + syntax::AssignmentOp::And => { + state.write("&"); + } + syntax::AssignmentOp::Xor => { + state.write("^"); + } + syntax::AssignmentOp::Or => { + state.write("|"); + } + } + show_hir_expr(state, &e); + state.write(","); + show_hir_expr(state, &v); + state.write(")"); + } else { + show_assignment_op(state, &op); + state.write(" "); + if force_scalar { + state.write("force_scalar("); + } + show_hir_expr(state, &e); + } + + if force_scalar { + state.write(")"); + } + } + } + hir::ExprKind::Bracket(ref e, ref indx) => { + show_hir_expr(state, &e); + state.write("["); + show_hir_expr(state, indx); + state.write("]"); + } + hir::ExprKind::FunCall(ref fun, ref args) => { + let mut cond_mask: u32 = 0; + let mut adapt_mask: u32 = 0; + let mut has_ret = false; + let mut array_constructor = false; + + let mut arg_mask: u32 = 0; + for (idx, e) in args.iter().enumerate() { + if expr_run_class(state, e) == hir::RunClass::Vector { + arg_mask |= 1 << idx; + } + } + + match fun { + hir::FunIdentifier::Constructor(t) => { + let is_scalar = state.is_scalar.replace(arg_mask == 0); + show_type(state, t); + state.is_scalar.set(is_scalar); + array_constructor = t.array_sizes.is_some(); + } + hir::FunIdentifier::Identifier(name) => { + if state.output_cxx { + let sym = state.hir.sym(*name); + match &sym.decl { + hir::SymDecl::NativeFunction(..) => { + if sym.name == "texelFetchOffset" && args.len() >= 4 { + if let Some((sampler, base, x, y)) = hir::get_texel_fetch_offset( + &state.hir, &args[0], &args[1], &args[3], + ) { + let base_sym = state.hir.sym(base); + if symbol_run_class(&base_sym.decl, state.vector_mask) + == hir::RunClass::Scalar + { + let sampler_sym = state.hir.sym(sampler); + add_used_global(state, &sampler); + if let hir::SymDecl::Global(..) = &base_sym.decl { + add_used_global(state, &base); + } + if y != 0 { + write!( + state, + "{}_{}_fetch[{}+{}*{}->stride]", + sampler_sym.name, + base_sym.name, + x, + y, + sampler_sym.name + ); + } else { + write!( + state, + "{}_{}_fetch[{}]", + sampler_sym.name, base_sym.name, x + ); + } + return; + } + } + } + show_sym(state, name) + } + hir::SymDecl::UserFunction(ref fd, ref _run_class) => { + if (state.mask.is_some() || state.return_declared) && + !fd.globals.is_empty() + { + cond_mask |= 1 << 31; + } + let mut param_mask: u32 = 0; + for (idx, (param, e)) in + fd.prototype.parameters.iter().zip(args.iter()).enumerate() + { + if let hir::FunctionParameterDeclaration::Named(qual, p) = param + { + if symbol_run_class(&state.hir.sym(p.sym).decl, arg_mask) + == hir::RunClass::Vector + { + param_mask |= 1 << idx; + } + match qual { + Some(hir::ParameterQualifier::InOut) + | Some(hir::ParameterQualifier::Out) => { + if state.mask.is_some() || state.return_declared { + cond_mask |= 1 << idx; + } + if (!arg_mask & param_mask & (1 << idx)) != 0 { + if adapt_mask == 0 { + state.write(if top_level { + "{ " + } else { + "({ " + }); + } + show_type(state, &p.ty); + write!(state, " _arg{}_ = ", idx); + show_hir_expr(state, e); + state.write("; "); + adapt_mask |= 1 << idx; + } + } + _ => {} + } + } + } + if adapt_mask != 0 && + fd.prototype.ty.kind != hir::TypeKind::Void && + !top_level + { + state.write("auto _ret_ = "); + has_ret = true; + } + show_sym(state, name); + let mut deps = state.deps.borrow_mut(); + let dep_key = ( + *name, + if cond_mask != 0 { + param_mask | (1 << 31) + } else { + param_mask + }, + ); + if !deps.contains(&dep_key) { + deps.push(dep_key); + } + } + hir::SymDecl::Struct(..) => { + show_sym(state, name); + if arg_mask == 0 { + state.write("_scalar"); + } + } + _ => panic!("bad identifier to function call"), + } + } + } + } + + if array_constructor { + state.write("{{"); + } else { + state.write("("); + } + + for (idx, e) in args.iter().enumerate() { + if idx != 0 { + state.write(", "); + } + if (adapt_mask & (1 << idx)) != 0 { + write!(state, "_arg{}_", idx); + } else { + show_hir_expr(state, e); + } + } + + if cond_mask != 0 { + if !args.is_empty() { + state.write(", "); + } + if let Some(mask) = &state.mask { + if state.return_declared { + state.write("("); + show_hir_expr(state, mask); + state.write(")&ret_mask"); + } else { + show_hir_expr(state, mask); + } + } else if state.return_declared { + state.write("ret_mask"); + } else { + state.write("~0"); + } + } + + if array_constructor { + state.write("}}"); + } else { + state.write(")"); + } + + if adapt_mask != 0 { + state.write("; "); + for (idx, e) in args.iter().enumerate() { + if (adapt_mask & (1 << idx)) != 0 { + state.is_lval.set(true); + show_hir_expr(state, e); + state.is_lval.set(false); + write!(state, " = force_scalar(_arg{}_); ", idx); + } + } + if has_ret { + state.write("_ret_; })"); + } else { + state.write(if top_level { "}" } else { "})" }); + } + } + } + hir::ExprKind::Dot(ref e, ref i) => { + state.write("("); + show_hir_expr(state, &e); + state.write(")"); + state.write("."); + show_identifier(state, i); + } + hir::ExprKind::SwizzleSelector(ref e, ref s) => { + if state.output_cxx { + state.write("("); + show_hir_expr(state, &e); + if state.is_lval.get() && s.components.len() > 1 { + state.write(").lsel("); + } else { + state.write(").sel("); + } + state.write(&s.to_args()); + state.write(")"); + } else { + state.write("("); + show_hir_expr(state, &e); + state.write(")"); + state.write("."); + state.write(&s.to_string()); + } + } + hir::ExprKind::PostInc(ref e) => { + show_hir_expr(state, &e); + state.write("++"); + } + hir::ExprKind::PostDec(ref e) => { + show_hir_expr(state, &e); + state.write("--"); + } + hir::ExprKind::Comma(ref a, ref b) => { + show_hir_expr(state, &a); + state.write(", "); + show_hir_expr(state, &b); + } + hir::ExprKind::Cond(index, _) => { + write!(state, "_c{}_", index); + } + hir::ExprKind::CondMask => { + state.write("_cond_mask_"); + } + } +} + +pub fn show_expr(state: &OutputState, expr: &syntax::Expr) { + match *expr { + syntax::Expr::Variable(ref i) => show_identifier(state, &i), + syntax::Expr::IntConst(ref x) => { + let _ = write!(state, "{}", x); + } + syntax::Expr::UIntConst(ref x) => { + let _ = write!(state, "{}u", x); + } + syntax::Expr::BoolConst(ref x) => { + let _ = write!(state, "{}", x); + } + syntax::Expr::FloatConst(ref x) => show_float(state, *x), + syntax::Expr::DoubleConst(ref x) => show_double(state, *x), + syntax::Expr::Unary(ref op, ref e) => { + show_unary_op(state, &op); + state.write("("); + show_expr(state, &e); + state.write(")"); + } + syntax::Expr::Binary(ref op, ref l, ref r) => { + state.write("("); + show_expr(state, &l); + state.write(")"); + show_binary_op(state, &op); + state.write("("); + show_expr(state, &r); + state.write(")"); + } + syntax::Expr::Ternary(ref c, ref s, ref e) => { + show_expr(state, &c); + state.write(" ? "); + show_expr(state, &s); + state.write(" : "); + show_expr(state, &e); + } + syntax::Expr::Assignment(ref v, ref op, ref e) => { + show_expr(state, &v); + state.write(" "); + show_assignment_op(state, &op); + state.write(" "); + show_expr(state, &e); + } + syntax::Expr::Bracket(ref e, ref a) => { + show_expr(state, &e); + show_array_spec(state, &a); + } + syntax::Expr::FunCall(ref fun, ref args) => { + show_function_identifier(state, &fun); + state.write("("); + + if !args.is_empty() { + let mut args_iter = args.iter(); + let first = args_iter.next().unwrap(); + show_expr(state, first); + + for e in args_iter { + state.write(", "); + show_expr(state, e); + } + } + + state.write(")"); + } + syntax::Expr::Dot(ref e, ref i) => { + state.write("("); + show_expr(state, &e); + state.write(")"); + state.write("."); + show_identifier(state, &i); + } + syntax::Expr::PostInc(ref e) => { + show_expr(state, &e); + state.write("++"); + } + syntax::Expr::PostDec(ref e) => { + show_expr(state, &e); + state.write("--"); + } + syntax::Expr::Comma(ref a, ref b) => { + show_expr(state, &a); + state.write(", "); + show_expr(state, &b); + } + } +} + +pub fn show_unary_op(state: &OutputState, op: &syntax::UnaryOp) { + match *op { + syntax::UnaryOp::Inc => { + state.write("++"); + } + syntax::UnaryOp::Dec => { + state.write("--"); + } + syntax::UnaryOp::Add => { + state.write("+"); + } + syntax::UnaryOp::Minus => { + state.write("-"); + } + syntax::UnaryOp::Not => { + state.write("!"); + } + syntax::UnaryOp::Complement => { + state.write("~"); + } + } +} + +pub fn show_binary_op(state: &OutputState, op: &syntax::BinaryOp) { + match *op { + syntax::BinaryOp::Or => { + state.write("||"); + } + syntax::BinaryOp::Xor => { + state.write("^^"); + } + syntax::BinaryOp::And => { + state.write("&&"); + } + syntax::BinaryOp::BitOr => { + state.write("|"); + } + syntax::BinaryOp::BitXor => { + state.write("^"); + } + syntax::BinaryOp::BitAnd => { + state.write("&"); + } + syntax::BinaryOp::Equal => { + state.write("=="); + } + syntax::BinaryOp::NonEqual => { + state.write("!="); + } + syntax::BinaryOp::LT => { + state.write("<"); + } + syntax::BinaryOp::GT => { + state.write(">"); + } + syntax::BinaryOp::LTE => { + state.write("<="); + } + syntax::BinaryOp::GTE => { + state.write(">="); + } + syntax::BinaryOp::LShift => { + state.write("<<"); + } + syntax::BinaryOp::RShift => { + state.write(">>"); + } + syntax::BinaryOp::Add => { + state.write("+"); + } + syntax::BinaryOp::Sub => { + state.write("-"); + } + syntax::BinaryOp::Mult => { + state.write("*"); + } + syntax::BinaryOp::Div => { + state.write("/"); + } + syntax::BinaryOp::Mod => { + state.write("%"); + } + } +} + +pub fn show_assignment_op(state: &OutputState, op: &syntax::AssignmentOp) { + match *op { + syntax::AssignmentOp::Equal => { + state.write("="); + } + syntax::AssignmentOp::Mult => { + state.write("*="); + } + syntax::AssignmentOp::Div => { + state.write("/="); + } + syntax::AssignmentOp::Mod => { + state.write("%="); + } + syntax::AssignmentOp::Add => { + state.write("+="); + } + syntax::AssignmentOp::Sub => { + state.write("-="); + } + syntax::AssignmentOp::LShift => { + state.write("<<="); + } + syntax::AssignmentOp::RShift => { + state.write(">>="); + } + syntax::AssignmentOp::And => { + state.write("&="); + } + syntax::AssignmentOp::Xor => { + state.write("^="); + } + syntax::AssignmentOp::Or => { + state.write("|="); + } + } +} + +pub fn show_function_identifier(state: &OutputState, i: &syntax::FunIdentifier) { + match *i { + syntax::FunIdentifier::Identifier(ref n) => show_identifier(state, &n), + syntax::FunIdentifier::Expr(ref e) => show_expr(state, &*e), + } +} + +pub fn show_hir_function_identifier(state: &OutputState, i: &hir::FunIdentifier) { + match *i { + hir::FunIdentifier::Identifier(ref n) => show_sym(state, n), + hir::FunIdentifier::Constructor(ref t) => show_type(state, &*t), + } +} + +pub fn show_declaration(state: &mut OutputState, d: &hir::Declaration) { + show_indent(state); + match *d { + hir::Declaration::FunctionPrototype(ref proto) => { + if !state.output_cxx { + show_function_prototype(state, &proto); + state.write(";\n"); + } + } + hir::Declaration::InitDeclaratorList(ref list) => { + show_init_declarator_list(state, &list); + state.write(";\n"); + + if state.output_cxx { + let base = list.head.name; + let base_sym = state.hir.sym(base); + if let hir::SymDecl::Local(..) = &base_sym.decl { + if symbol_run_class(&base_sym.decl, state.vector_mask) == hir::RunClass::Scalar + { + let mut texel_fetches = state.texel_fetches.borrow_mut(); + while let Some(idx) = texel_fetches.iter().position(|&(_, b, _)| b == base) + { + let (sampler, _, offsets) = texel_fetches.remove(idx); + let sampler_sym = state.hir.sym(sampler); + define_texel_fetch_ptr(state, &base_sym, &sampler_sym, &offsets); + } + } + } + } + } + hir::Declaration::Precision(ref qual, ref ty) => { + if !state.output_cxx { + show_precision_qualifier(state, &qual); + show_type_specifier(state, &ty); + state.write(";\n"); + } + } + hir::Declaration::Block(ref _block) => { + panic!(); + //show_block(state, &block); + //state.write(";\n"); + } + hir::Declaration::Global(ref qual, ref identifiers) => { + show_type_qualifier(state, &qual); + + if !identifiers.is_empty() { + let mut iter = identifiers.iter(); + let first = iter.next().unwrap(); + show_identifier(state, first); + + for identifier in iter { + let _ = write!(state, ", {}", identifier); + } + } + + state.write(";\n"); + } + hir::Declaration::StructDefinition(ref sym) => { + show_sym_decl(state, sym); + + state.write(";\n"); + } + } +} + +pub fn show_function_prototype(state: &mut OutputState, fp: &hir::FunctionPrototype) { + let is_scalar = state.is_scalar.replace(!state.return_vector); + show_type(state, &fp.ty); + state.is_scalar.set(is_scalar); + + state.write(" "); + show_identifier(state, &fp.name); + + state.write("("); + + if !fp.parameters.is_empty() { + let mut iter = fp.parameters.iter(); + let first = iter.next().unwrap(); + show_function_parameter_declaration(state, first); + + for param in iter { + state.write(", "); + show_function_parameter_declaration(state, param); + } + } + + if state.output_cxx && (state.vector_mask & (1 << 31)) != 0 { + if !fp.parameters.is_empty() { + state.write(", "); + } + state.write("I32 _cond_mask_"); + } + + state.write(")"); +} + +pub fn show_function_parameter_declaration( + state: &mut OutputState, + p: &hir::FunctionParameterDeclaration, +) { + match *p { + hir::FunctionParameterDeclaration::Named(ref qual, ref fpd) => { + if state.output_cxx { + let is_scalar = state.is_scalar.replace( + symbol_run_class(&state.hir.sym(fpd.sym).decl, state.vector_mask) + == hir::RunClass::Scalar, + ); + show_type(state, &fpd.ty); + state.is_scalar.set(is_scalar); + show_parameter_qualifier(state, qual); + } else { + show_parameter_qualifier(state, qual); + state.write(" "); + show_type(state, &fpd.ty); + } + state.write(" "); + show_identifier_and_type(state, &fpd.name, &fpd.ty); + } + hir::FunctionParameterDeclaration::Unnamed(ref qual, ref ty) => { + if state.output_cxx { + show_type_specifier(state, ty); + show_parameter_qualifier(state, qual); + } else { + show_parameter_qualifier(state, qual); + state.write(" "); + show_type_specifier(state, ty); + } + } + } +} + +pub fn show_init_declarator_list(state: &mut OutputState, i: &hir::InitDeclaratorList) { + show_single_declaration(state, &i.head); + + for decl in &i.tail { + state.write(", "); + show_single_declaration_no_type(state, decl); + } +} + +pub fn show_single_declaration(state: &mut OutputState, d: &hir::SingleDeclaration) { + if state.output_cxx { + show_single_declaration_cxx(state, d) + } else { + show_single_declaration_glsl(state, d) + } +} + +pub fn show_single_declaration_glsl(state: &mut OutputState, d: &hir::SingleDeclaration) { + if let Some(ref qual) = d.qualifier { + show_type_qualifier(state, &qual); + state.write(" "); + } + + let sym = state.hir.sym(d.name); + match &sym.decl { + hir::SymDecl::Global(storage, interpolation, ..) => { + show_storage_class(state, storage); + if let Some(i) = interpolation { + show_interpolation_qualifier(state, i); + } + } + hir::SymDecl::Local(storage, ..) => show_storage_class(state, storage), + _ => panic!("should be variable"), + } + + if let Some(ty_def) = d.ty_def { + show_sym_decl(state, &ty_def); + } else { + show_type(state, &d.ty); + } + + state.write(" "); + state.write(sym.name.as_str()); + + if let Some(ref arr_spec) = d.ty.array_sizes { + show_array_sizes(state, &arr_spec); + } + + if let Some(ref initializer) = d.initializer { + state.write(" = "); + show_initializer(state, initializer); + } +} + +fn symbol_run_class(decl: &hir::SymDecl, vector_mask: u32) -> hir::RunClass { + let run_class = match decl { + hir::SymDecl::Global(_, _, _, run_class) => *run_class, + hir::SymDecl::Local(_, _, run_class) => *run_class, + _ => hir::RunClass::Vector, + }; + match run_class { + hir::RunClass::Scalar => hir::RunClass::Scalar, + hir::RunClass::Dependent(mask) => { + if (mask & vector_mask) != 0 { + hir::RunClass::Vector + } else { + hir::RunClass::Scalar + } + } + _ => hir::RunClass::Vector, + } +} + +pub fn show_single_declaration_cxx(state: &mut OutputState, d: &hir::SingleDeclaration) { + let sym = state.hir.sym(d.name); + match &sym.decl { + hir::SymDecl::Global(hir::StorageClass::Sampler(format), ..) => { + write!( + state, + "{}{} {}", + d.ty.kind.cxx_primitive_type_name().unwrap(), + format.type_suffix().unwrap_or(""), + sym.name.as_str() + ); + return; + } + _ => {} + } + if state.kind == ShaderKind::Fragment { + match &sym.decl { + hir::SymDecl::Global(hir::StorageClass::FragColor(index), ..) => { + let fragcolor = match index { + 0 => "gl_FragColor", + 1 => "gl_SecondaryFragColor", + _ => panic!(), + }; + write!(state, "#define {} {}\n", sym.name, fragcolor); + show_indent(state); + state.write("// "); + } + hir::SymDecl::Global(hir::StorageClass::Out, ..) => { + write!(state, "#define {} gl_FragColor\n", sym.name); + show_indent(state); + state.write("// "); + } + _ => {} + } + } + let is_scalar = state + .is_scalar + .replace(symbol_run_class(&sym.decl, state.vector_mask) == hir::RunClass::Scalar); + + if let Some(ref _array) = d.ty.array_sizes { + show_type(state, &d.ty); + } else { + if let Some(ty_def) = d.ty_def { + show_sym_decl(state, &ty_def); + } else { + show_type(state, &d.ty); + } + } + + // XXX: this is pretty grotty + state.write(" "); + show_sym_decl(state, &d.name); + + state.is_scalar.set(is_scalar); + + if let Some(ref initializer) = d.initializer { + state.write(" = "); + show_initializer(state, initializer); + } +} + +pub fn show_single_declaration_no_type(state: &OutputState, d: &hir::SingleDeclarationNoType) { + show_arrayed_identifier(state, &d.ident); + + if let Some(ref initializer) = d.initializer { + state.write(" = "); + show_initializer(state, initializer); + } +} + +pub fn show_initializer(state: &OutputState, i: &hir::Initializer) { + match *i { + hir::Initializer::Simple(ref e) => show_hir_expr(state, e), + hir::Initializer::List(ref list) => { + let mut iter = list.0.iter(); + let first = iter.next().unwrap(); + + state.write("{ "); + show_initializer(state, first); + + for ini in iter { + state.write(", "); + show_initializer(state, ini); + } + + state.write(" }"); + } + } +} + +/* +pub fn show_block(state: &mut OutputState, b: &hir::Block) { + show_type_qualifier(state, &b.qualifier); + state.write(" "); + show_identifier(state, &b.name); + state.write(" {"); + + for field in &b.fields { + show_struct_field(state, field); + state.write("\n"); + } + state.write("}"); + + if let Some(ref ident) = b.identifier { + show_arrayed_identifier(state, ident); + } +} +*/ + +// This is a hack to run through the first time with an empty writter to find if 'return' is declared. +pub fn has_conditional_return(state: &mut OutputState, cst: &hir::CompoundStatement) -> bool { + let buffer = state.push_buffer(); + show_compound_statement(state, cst); + state.pop_buffer(buffer); + let result = state.return_declared; + state.return_declared = false; + result +} + +fn define_texel_fetch_ptr( + state: &OutputState, + base_sym: &hir::Symbol, + sampler_sym: &hir::Symbol, + offsets: &hir::TexelFetchOffsets, +) { + show_indent(state); + if let hir::SymDecl::Global(_, _, ty, _) = &sampler_sym.decl { + match ty.kind { + hir::TypeKind::Sampler2D => { + write!( + state, + "vec4_scalar* {}_{}_fetch = ", + sampler_sym.name, base_sym.name + ); + } + hir::TypeKind::ISampler2D => { + write!( + state, + "ivec4_scalar* {}_{}_fetch = ", + sampler_sym.name, base_sym.name + ); + } + _ => panic!(), + } + } else { + panic!(); + } + write!( + state, + "texelFetchPtr({}, {}, {}, {}, {}, {});\n", + sampler_sym.name, base_sym.name, offsets.min_x, offsets.max_x, offsets.min_y, offsets.max_y + ); +} + +pub fn show_function_definition( + state: &mut OutputState, + fd: &hir::FunctionDefinition, + vector_mask: u32, +) { + // println!("start {:?} {:?}", fd.prototype.name, vector_mask); + if state.output_cxx && fd.prototype.name.as_str() == "main" { + state.write("ALWAYS_INLINE "); + } + show_function_prototype(state, &fd.prototype); + state.write(" "); + state.return_type = Some(Box::new(fd.prototype.ty.clone())); + + if state.output_cxx && (vector_mask & (1 << 31)) != 0 { + state.mask = Some(Box::new(hir::Expr { + kind: hir::ExprKind::CondMask, + ty: hir::Type::new(hir::TypeKind::Bool), + })); + } + + show_indent(state); + state.write("{\n"); + + state.indent(); + if has_conditional_return(state, &fd.body) { + show_indent(state); + state.write(if state.return_vector { + "I32" + } else { + "int32_t" + }); + state.write(" ret_mask = "); + if let Some(mask) = &state.mask { + show_hir_expr(state, mask); + } else { + state.write("~0"); + } + state.write(";\n"); + // XXX: the cloning here is bad + show_indent(state); + if fd.prototype.ty != Type::new(hir::TypeKind::Void) { + let is_scalar = state.is_scalar.replace(!state.return_vector); + show_type(state, &state.return_type.clone().unwrap()); + state.write(" ret;\n"); + state.is_scalar.set(is_scalar); + } + } + + if state.output_cxx { + let mut texel_fetches = state.texel_fetches.borrow_mut(); + texel_fetches.clear(); + for ((sampler, base), offsets) in fd.texel_fetches.iter() { + let base_sym = state.hir.sym(*base); + if symbol_run_class(&base_sym.decl, vector_mask) == hir::RunClass::Scalar { + add_used_global(state, sampler); + let sampler_sym = state.hir.sym(*sampler); + match &base_sym.decl { + hir::SymDecl::Global(..) => { + add_used_global(state, base); + define_texel_fetch_ptr(state, &base_sym, &sampler_sym, &offsets); + } + hir::SymDecl::Local(..) => { + if fd.prototype.has_parameter(*base) { + define_texel_fetch_ptr(state, &base_sym, &sampler_sym, &offsets); + } else { + texel_fetches.push((*sampler, *base, offsets.clone())); + } + } + _ => panic!(), + } + } + } + } + + for st in &fd.body.statement_list { + show_statement(state, st); + } + + if state.return_declared { + show_indent(state); + if fd.prototype.ty == Type::new(hir::TypeKind::Void) { + state.write("return;\n"); + } else { + state.write("return ret;\n"); + } + } + state.outdent(); + + show_indent(state); + state.write("}\n"); + // println!("end {:?}", fd.prototype.name); + + state.return_type = None; + state.return_declared = false; + state.mask = None; +} + +pub fn show_compound_statement(state: &mut OutputState, cst: &hir::CompoundStatement) { + show_indent(state); + state.write("{\n"); + + state.indent(); + for st in &cst.statement_list { + show_statement(state, st); + } + state.outdent(); + + show_indent(state); + state.write("}\n"); +} + +pub fn show_statement(state: &mut OutputState, st: &hir::Statement) { + match *st { + hir::Statement::Compound(ref cst) => show_compound_statement(state, cst), + hir::Statement::Simple(ref sst) => show_simple_statement(state, sst), + } +} + +pub fn show_simple_statement(state: &mut OutputState, sst: &hir::SimpleStatement) { + match *sst { + hir::SimpleStatement::Declaration(ref d) => show_declaration(state, d), + hir::SimpleStatement::Expression(ref e) => show_expression_statement(state, e), + hir::SimpleStatement::Selection(ref s) => show_selection_statement(state, s), + hir::SimpleStatement::Switch(ref s) => show_switch_statement(state, s), + hir::SimpleStatement::Iteration(ref i) => show_iteration_statement(state, i), + hir::SimpleStatement::Jump(ref j) => show_jump_statement(state, j), + } +} + +pub fn show_indent(state: &OutputState) { + for _ in 0 .. state.indent { + state.write(" "); + } +} + +pub fn show_expression_statement(state: &mut OutputState, est: &hir::ExprStatement) { + show_indent(state); + + if let Some(ref e) = *est { + show_hir_expr_inner(state, e, true); + } + + state.write(";\n"); +} + +pub fn show_selection_statement(state: &mut OutputState, sst: &hir::SelectionStatement) { + show_indent(state); + + if state.output_cxx && + (state.return_declared || expr_run_class(state, &sst.cond) != hir::RunClass::Scalar) + { + let (cond_index, mask) = if state.mask.is_none() || sst.else_stmt.is_some() { + let cond = sst.cond.clone(); + state.cond_index += 1; + let cond_index = state.cond_index; + write!(state, "auto _c{}_ = ", cond_index); + show_hir_expr(state, &cond); + state.write(";\n"); + ( + cond_index, + Box::new(hir::Expr { + kind: hir::ExprKind::Cond(cond_index, cond), + ty: hir::Type::new(hir::TypeKind::Bool), + }), + ) + } else { + (0, sst.cond.clone()) + }; + + let previous = mem::replace(&mut state.mask, None); + state.mask = Some(match previous.clone() { + Some(e) => { + let cond = Box::new(hir::Expr { + kind: hir::ExprKind::Binary(syntax::BinaryOp::BitAnd, e, mask.clone()), + ty: hir::Type::new(hir::TypeKind::Bool), + }); + state.cond_index += 1; + let nested_cond_index = state.cond_index; + show_indent(state); + write!(state, "auto _c{}_ = ", nested_cond_index); + show_hir_expr(state, &cond); + state.write(";\n"); + Box::new(hir::Expr { + kind: hir::ExprKind::Cond(nested_cond_index, cond), + ty: hir::Type::new(hir::TypeKind::Bool), + }) + } + None => mask.clone(), + }); + + show_statement(state, &sst.body); + state.mask = previous; + + if let Some(rest) = &sst.else_stmt { + // invert the condition + let inverted_cond = Box::new(hir::Expr { + kind: hir::ExprKind::Unary(UnaryOp::Complement, mask), + ty: hir::Type::new(hir::TypeKind::Bool), + }); + let previous = mem::replace(&mut state.mask, None); + state.mask = Some(match previous.clone() { + Some(e) => { + let cond = Box::new(hir::Expr { + kind: hir::ExprKind::Binary(syntax::BinaryOp::BitAnd, e, inverted_cond), + ty: hir::Type::new(hir::TypeKind::Bool), + }); + show_indent(state); + write!(state, "_c{}_ = ", cond_index); + show_hir_expr(state, &cond); + state.write(";\n"); + Box::new(hir::Expr { + kind: hir::ExprKind::Cond(cond_index, cond), + ty: hir::Type::new(hir::TypeKind::Bool), + }) + } + None => inverted_cond, + }); + + show_statement(state, rest); + state.mask = previous; + } + } else { + state.write("if ("); + show_hir_expr(state, &sst.cond); + state.write(") {\n"); + + state.indent(); + show_statement(state, &sst.body); + state.outdent(); + + show_indent(state); + if let Some(rest) = &sst.else_stmt { + state.write("} else "); + show_statement(state, rest); + } else { + state.write("}\n"); + } + } +} + +fn case_stmts_to_if_stmts(stmts: &[Statement], last: bool) -> (Option>, bool) { + // Look for jump statements and remove them + // We currently are pretty strict on the form that the statement + // list needs to be in. This can be loosened as needed. + let mut fallthrough = false; + let cstmt = match &stmts[..] { + [hir::Statement::Compound(c)] => match c.statement_list.split_last() { + Some((hir::Statement::Simple(s), rest)) => match **s { + hir::SimpleStatement::Jump(hir::JumpStatement::Break) => hir::CompoundStatement { + statement_list: rest.to_owned(), + }, + _ => panic!("fall through not supported"), + }, + _ => panic!("empty compound"), + }, + [hir::Statement::Simple(s)] => { + match **s { + hir::SimpleStatement::Jump(hir::JumpStatement::Break) => hir::CompoundStatement { + statement_list: Vec::new(), + }, + _ => { + if last { + // we don't need a break at the end + hir::CompoundStatement { + statement_list: vec![hir::Statement::Simple(s.clone())], + } + } else { + panic!("fall through not supported {:?}", s) + } + } + } + } + [] => return (None, true), + stmts => match stmts.split_last() { + Some((hir::Statement::Simple(s), rest)) => match **s { + hir::SimpleStatement::Jump(hir::JumpStatement::Break) => hir::CompoundStatement { + statement_list: rest.to_owned(), + }, + _ => { + if !last { + fallthrough = true; + } + hir::CompoundStatement { + statement_list: stmts.to_owned(), + } + } + }, + _ => panic!("unexpected empty"), + }, + }; + let stmts = Box::new(hir::Statement::Compound(Box::new(cstmt))); + (Some(stmts), fallthrough) +} + +fn build_selection<'a, I: Iterator>( + head: &Box, + case: &hir::Case, + mut cases: I, + default: Option<&hir::Case>, + previous_condition: Option>, + previous_stmts: Option>, +) -> hir::SelectionStatement { + let cond = match &case.label { + hir::CaseLabel::Case(e) => Some(Box::new(hir::Expr { + kind: hir::ExprKind::Binary(syntax::BinaryOp::Equal, head.clone(), e.clone()), + ty: hir::Type::new(hir::TypeKind::Bool), + })), + hir::CaseLabel::Def => None, + }; + + // if we have two conditions join them + let cond = match (&previous_condition, &cond) { + (Some(prev), Some(cond)) => Some(Box::new(hir::Expr { + kind: hir::ExprKind::Binary(syntax::BinaryOp::Or, prev.clone(), cond.clone()), + ty: hir::Type::new(hir::TypeKind::Bool), + })), + (_, cond) => cond.clone(), + }; + + /* + + // find the next case that's not a default + let next_case = loop { + match cases.next() { + Some(hir::Case { label: hir::CaseLabel::Def, ..}) => { }, + case => break case, + } + };*/ + + let (cond, body, else_stmt) = match (cond, cases.next()) { + (None, Some(next_case)) => { + assert!(previous_stmts.is_none()); + // default so just move on to the next + return build_selection(head, next_case, cases, default, None, None); + } + (Some(cond), Some(next_case)) => { + assert!(previous_stmts.is_none()); + let (stmts, fallthrough) = case_stmts_to_if_stmts(&case.stmts, false); + if !fallthrough && stmts.is_some() { + ( + cond, + stmts.unwrap(), + Some(Box::new(hir::Statement::Simple(Box::new( + hir::SimpleStatement::Selection(build_selection( + head, next_case, cases, default, None, None, + )), + )))), + ) + } else { + // empty so fall through to the next + return build_selection(head, next_case, cases, default, Some(cond), stmts); + } + } + (Some(cond), None) => { + // non-default last + assert!(previous_stmts.is_none()); + let (stmts, _) = case_stmts_to_if_stmts(&case.stmts, default.is_none()); + let stmts = stmts.expect("empty case labels unsupported at the end"); + // add the default case at the end if we have one + ( + cond, + stmts, + match default { + Some(default) => { + let (default_stmts, fallthrough) = + case_stmts_to_if_stmts(&default.stmts, true); + assert!(!fallthrough); + Some(default_stmts.expect("empty default unsupported")) + } + None => None, + }, + ) + } + (None, None) => { + // default, last + + assert!(default.is_some()); + + let (stmts, fallthrough) = case_stmts_to_if_stmts(&case.stmts, true); + let stmts = stmts.expect("empty default unsupported"); + assert!(!fallthrough); + + match previous_stmts { + Some(previous_stmts) => { + let cond = previous_condition.expect("must have previous condition"); + (cond, previous_stmts, Some(stmts)) + } + None => { + let cond = Box::new(hir::Expr { + kind: hir::ExprKind::BoolConst(true), + ty: hir::Type::new(hir::TypeKind::Bool), + }); + (cond, stmts, None) + } + } + } + }; + + hir::SelectionStatement { + cond, + body, + else_stmt, + } +} + +pub fn lower_switch_to_ifs(sst: &hir::SwitchStatement) -> hir::SelectionStatement { + let default = sst.cases.iter().find(|x| x.label == hir::CaseLabel::Def); + let mut cases = sst.cases.iter(); + let r = build_selection(&sst.head, cases.next().unwrap(), cases, default, None, None); + r +} + +fn is_declaration(stmt: &hir::Statement) -> bool { + if let hir::Statement::Simple(s) = stmt { + if let hir::SimpleStatement::Declaration(..) = **s { + return true; + } + } + return false; +} + +pub fn show_switch_statement(state: &mut OutputState, sst: &hir::SwitchStatement) { + if state.output_cxx && expr_run_class(state, &sst.head) != hir::RunClass::Scalar { + // XXX: when lowering switches we end up with a mask that has + // a bunch of mutually exclusive conditions. + // It would be nice if we could fold them together. + let ifs = lower_switch_to_ifs(sst); + return show_selection_statement(state, &ifs); + } + + show_indent(state); + state.write("switch ("); + show_hir_expr(state, &sst.head); + state.write(") {\n"); + state.indent(); + + for case in &sst.cases { + show_case_label(state, &case.label); + state.indent(); + + let has_declaration = case.stmts.iter().any(|x| is_declaration(x)); + // glsl allows declarations in switch statements while C requires them to be + // in a compound statement. If we have a declaration wrap the statements in an block. + // This will break some glsl shaders but keeps the saner ones working + if has_declaration { + show_indent(state); + state.write("{\n"); + state.indent(); + } + for st in &case.stmts { + show_statement(state, st); + } + + if has_declaration { + show_indent(state); + state.write("}\n"); + state.outdent(); + } + + state.outdent(); + } + state.outdent(); + show_indent(state); + state.write("}\n"); +} + +pub fn show_case_label(state: &mut OutputState, cl: &hir::CaseLabel) { + show_indent(state); + match *cl { + hir::CaseLabel::Case(ref e) => { + state.write("case "); + show_hir_expr(state, e); + state.write(":\n"); + } + hir::CaseLabel::Def => { + state.write("default:\n"); + } + } +} + +pub fn show_iteration_statement(state: &mut OutputState, ist: &hir::IterationStatement) { + show_indent(state); + match *ist { + hir::IterationStatement::While(ref cond, ref body) => { + state.write("while ("); + show_condition(state, cond); + state.write(") "); + show_statement(state, body); + } + hir::IterationStatement::DoWhile(ref body, ref cond) => { + state.write("do "); + show_statement(state, body); + state.write(" while ("); + show_hir_expr(state, cond); + state.write(")\n"); + } + hir::IterationStatement::For(ref init, ref rest, ref body) => { + state.write("for ("); + show_for_init_statement(state, init); + show_for_rest_statement(state, rest); + state.write(") "); + show_statement(state, body); + } + } +} + +pub fn show_condition(state: &mut OutputState, c: &hir::Condition) { + match *c { + hir::Condition::Expr(ref e) => show_hir_expr(state, e), + /*hir::Condition::Assignment(ref ty, ref name, ref initializer) => { + show_type(state, ty); + state.write(" "); + show_identifier(f, name); + state.write(" = "); + show_initializer(state, initializer); + }*/ + } +} + +pub fn show_for_init_statement(state: &mut OutputState, i: &hir::ForInitStatement) { + match *i { + hir::ForInitStatement::Expression(ref expr) => { + if let Some(ref e) = *expr { + show_hir_expr(state, e); + } + } + hir::ForInitStatement::Declaration(ref d) => { + show_declaration(state, d); + } + } +} + +pub fn show_for_rest_statement(state: &mut OutputState, r: &hir::ForRestStatement) { + if let Some(ref cond) = r.condition { + show_condition(state, cond); + } + + state.write("; "); + + if let Some(ref e) = r.post_expr { + show_hir_expr(state, e); + } +} + +fn use_return_mask(state: &OutputState) -> bool { + if let Some(mask) = &state.mask { + mask.kind != hir::ExprKind::CondMask + } else { + false + } +} + +pub fn show_jump_statement(state: &mut OutputState, j: &hir::JumpStatement) { + show_indent(state); + match *j { + hir::JumpStatement::Continue => { + state.write("continue;\n"); + } + hir::JumpStatement::Break => { + state.write("break;\n"); + } + hir::JumpStatement::Discard => { + if state.output_cxx { + state.uses_discard = true; + if let Some(mask) = &state.mask { + state.write("isPixelDiscarded |= ("); + show_hir_expr(state, mask); + state.write(")"); + if state.return_declared { + state.write("&ret_mask"); + } + state.write(";\n"); + } else { + state.write("isPixelDiscarded = true;\n"); + } + } else { + state.write("discard;\n"); + } + } + hir::JumpStatement::Return(ref e) => { + if let Some(e) = e { + if state.output_cxx { + if use_return_mask(state) { + // We cast any conditions by `ret_mask_type` so that scalars nicely + // convert to -1. i.e. I32 &= bool will give the wrong result. while I32 &= I32(bool) works + let ret_mask_type = if state.return_vector { + "I32" + } else { + "int32_t" + }; + if state.return_declared { + // XXX: the cloning here is bad + write!(state, "ret = if_then_else(ret_mask & {}(", ret_mask_type); + show_hir_expr(state, &state.mask.clone().unwrap()); + state.write("), "); + show_hir_expr(state, e); + state.write(", ret);\n"); + } else { + state.write("ret = "); + show_hir_expr(state, e); + state.write(";\n"); + } + + show_indent(state); + + if state.return_declared { + write!(state, "ret_mask &= ~{}(", ret_mask_type); + } else { + write!(state, "ret_mask = ~{}(", ret_mask_type); + } + show_hir_expr(state, &state.mask.clone().unwrap()); + state.write(");\n"); + state.return_declared = true; + } else { + if state.return_declared { + state.write("ret = if_then_else(ret_mask, "); + show_hir_expr(state, e); + state.write(", ret);\n"); + } else { + state.write("return "); + show_hir_expr(state, e); + state.write(";\n"); + } + } + } else { + state.write("return "); + show_hir_expr(state, e); + state.write(";\n"); + } + } else { + if state.output_cxx { + if use_return_mask(state) { + show_indent(state); + let ret_mask_type = if state.return_vector { + "I32" + } else { + "int32_t" + }; + if state.return_declared { + write!(state, "ret_mask &= ~{}(", ret_mask_type); + } else { + write!(state, "ret_mask = ~{}(", ret_mask_type); + } + show_hir_expr(state, &state.mask.clone().unwrap()); + state.write(");\n"); + state.return_declared = true; + } else { + state.write("return;\n"); + } + } else { + state.write("return;\n"); + } + } + } + } +} + +pub fn show_path(state: &OutputState, path: &syntax::Path) { + match path { + syntax::Path::Absolute(s) => { + let _ = write!(state, "<{}>", s); + } + syntax::Path::Relative(s) => { + let _ = write!(state, "\"{}\"", s); + } + } +} + +pub fn show_preprocessor(state: &OutputState, pp: &syntax::Preprocessor) { + match *pp { + syntax::Preprocessor::Define(ref pd) => show_preprocessor_define(state, pd), + syntax::Preprocessor::Else => show_preprocessor_else(state), + syntax::Preprocessor::ElseIf(ref pei) => show_preprocessor_elseif(state, pei), + syntax::Preprocessor::EndIf => show_preprocessor_endif(state), + syntax::Preprocessor::Error(ref pe) => show_preprocessor_error(state, pe), + syntax::Preprocessor::If(ref pi) => show_preprocessor_if(state, pi), + syntax::Preprocessor::IfDef(ref pid) => show_preprocessor_ifdef(state, pid), + syntax::Preprocessor::IfNDef(ref pind) => show_preprocessor_ifndef(state, pind), + syntax::Preprocessor::Include(ref pi) => show_preprocessor_include(state, pi), + syntax::Preprocessor::Line(ref pl) => show_preprocessor_line(state, pl), + syntax::Preprocessor::Pragma(ref pp) => show_preprocessor_pragma(state, pp), + syntax::Preprocessor::Undef(ref pu) => show_preprocessor_undef(state, pu), + syntax::Preprocessor::Version(ref pv) => show_preprocessor_version(state, pv), + syntax::Preprocessor::Extension(ref pe) => show_preprocessor_extension(state, pe), + } +} + +pub fn show_preprocessor_define(state: &OutputState, pd: &syntax::PreprocessorDefine) { + match *pd { + syntax::PreprocessorDefine::ObjectLike { + ref ident, + ref value, + } => { + let _ = write!(state, "#define {} {}\n", ident, value); + } + + syntax::PreprocessorDefine::FunctionLike { + ref ident, + ref args, + ref value, + } => { + let _ = write!(state, "#define {}(", ident); + + if !args.is_empty() { + let _ = write!(state, "{}", &args[0]); + + for arg in &args[1 .. args.len()] { + let _ = write!(state, ", {}", arg); + } + } + + let _ = write!(state, ") {}\n", value); + } + } +} + +pub fn show_preprocessor_else(state: &OutputState) { + state.write("#else\n"); +} + +pub fn show_preprocessor_elseif(state: &OutputState, pei: &syntax::PreprocessorElseIf) { + let _ = write!(state, "#elseif {}\n", pei.condition); +} + +pub fn show_preprocessor_error(state: &OutputState, pe: &syntax::PreprocessorError) { + let _ = writeln!(state, "#error {}", pe.message); +} + +pub fn show_preprocessor_endif(state: &OutputState) { + state.write("#endif\n"); +} + +pub fn show_preprocessor_if(state: &OutputState, pi: &syntax::PreprocessorIf) { + let _ = write!(state, "#if {}\n", pi.condition); +} + +pub fn show_preprocessor_ifdef(state: &OutputState, pid: &syntax::PreprocessorIfDef) { + state.write("#ifdef "); + show_identifier(state, &pid.ident); + state.write("\n"); +} + +pub fn show_preprocessor_ifndef(state: &OutputState, pind: &syntax::PreprocessorIfNDef) { + state.write("#ifndef "); + show_identifier(state, &pind.ident); + state.write("\n"); +} + +pub fn show_preprocessor_include(state: &OutputState, pi: &syntax::PreprocessorInclude) { + state.write("#include "); + show_path(state, &pi.path); + state.write("\n"); +} + +pub fn show_preprocessor_line(state: &OutputState, pl: &syntax::PreprocessorLine) { + let _ = write!(state, "#line {}", pl.line); + if let Some(source_string_number) = pl.source_string_number { + let _ = write!(state, " {}", source_string_number); + } + state.write("\n"); +} + +pub fn show_preprocessor_pragma(state: &OutputState, pp: &syntax::PreprocessorPragma) { + let _ = writeln!(state, "#pragma {}", pp.command); +} + +pub fn show_preprocessor_undef(state: &OutputState, pud: &syntax::PreprocessorUndef) { + state.write("#undef "); + show_identifier(state, &pud.name); + state.write("\n"); +} + +pub fn show_preprocessor_version(state: &OutputState, pv: &syntax::PreprocessorVersion) { + let _ = write!(state, "#version {}", pv.version); + + if let Some(ref profile) = pv.profile { + match *profile { + syntax::PreprocessorVersionProfile::Core => { + state.write(" core"); + } + syntax::PreprocessorVersionProfile::Compatibility => { + state.write(" compatibility"); + } + syntax::PreprocessorVersionProfile::ES => { + state.write(" es"); + } + } + } + + state.write("\n"); +} + +pub fn show_preprocessor_extension(state: &OutputState, pe: &syntax::PreprocessorExtension) { + state.write("#extension "); + + match pe.name { + syntax::PreprocessorExtensionName::All => { + state.write("all"); + } + syntax::PreprocessorExtensionName::Specific(ref n) => { + state.write(n); + } + } + + if let Some(ref behavior) = pe.behavior { + match *behavior { + syntax::PreprocessorExtensionBehavior::Require => { + state.write(" : require"); + } + syntax::PreprocessorExtensionBehavior::Enable => { + state.write(" : enable"); + } + syntax::PreprocessorExtensionBehavior::Warn => { + state.write(" : warn"); + } + syntax::PreprocessorExtensionBehavior::Disable => { + state.write(" : disable"); + } + } + } + + state.write("\n"); +} + +pub fn show_external_declaration(state: &mut OutputState, ed: &hir::ExternalDeclaration) { + match *ed { + hir::ExternalDeclaration::Preprocessor(ref pp) => { + if !state.output_cxx { + show_preprocessor(state, pp) + } + } + hir::ExternalDeclaration::FunctionDefinition(ref fd) => { + if !state.output_cxx { + show_function_definition(state, fd, !0) + } + } + hir::ExternalDeclaration::Declaration(ref d) => show_declaration(state, d), + } +} + +pub fn show_cxx_function_definition(state: &mut OutputState, name: hir::SymRef, vector_mask: u32) { + if let Some((ref fd, run_class)) = state.hir.function_definition(name) { + state.vector_mask = vector_mask; + state.return_vector = (vector_mask & (1 << 31)) != 0 + || match run_class { + hir::RunClass::Scalar => false, + hir::RunClass::Dependent(mask) => (mask & vector_mask) != 0, + _ => true, + }; + match state.functions.get(&(name, vector_mask)) { + Some(true) => {} + Some(false) => { + show_function_prototype(state, &fd.prototype); + state.functions.insert((name, vector_mask), true); + } + None => { + state.functions.insert((name, vector_mask), false); + let buffer = state.push_buffer(); + show_function_definition(state, fd, vector_mask); + for (name, vector_mask) in state.deps.replace(Vec::new()) { + show_cxx_function_definition(state, name, vector_mask); + } + state.flush_buffer(); + state.pop_buffer(buffer); + state.functions.insert((name, vector_mask), true); + } + } + } +} + +pub fn show_translation_unit(state: &mut OutputState, tu: &hir::TranslationUnit) { + state.flush_buffer(); + + for ed in &(tu.0).0 { + show_external_declaration(state, ed); + state.flush_buffer(); + } + if state.output_cxx { + if let Some(name) = state.hir.lookup("main") { + show_cxx_function_definition(state, name, 0); + state.flush_buffer(); + } + } +} + +fn write_abi(state: &mut OutputState) { + match state.kind { + ShaderKind::Fragment => { + state.write("static bool use_discard(Self*) { return "); + state.write(if state.uses_discard { "true" } else { "false" }); + state.write("; }\n"); + state.write("static void run(Self *self) {\n"); + if state.uses_discard { + state.write(" self->isPixelDiscarded = false;\n"); + } + state.write(" self->main();\n"); + state.write(" self->step_interp_inputs();\n"); + state.write("}\n"); + state.write("static void skip(Self* self, int chunks) {\n"); + state.write(" self->step_interp_inputs();\n"); + state.write(" while (--chunks > 0) self->step_interp_inputs();\n"); + state.write("}\n"); + if state.has_draw_span_rgba8 { + state.write( + "static void draw_span_RGBA8(Self* self, uint32_t* buf, int len) {\ + dispatch_draw_span(self, buf, len); }\n"); + } + if state.has_draw_span_r8 { + state.write( + "static void draw_span_R8(Self* self, uint8_t* buf, int len) {\ + dispatch_draw_span(self, buf, len); }\n"); + } + + write!(state, "{}_frag() {{\n", state.name); + } + ShaderKind::Vertex => { + state.write( + "static void run(Self* self, char* flats, char* interps, size_t interp_stride) {\n", + ); + state.write(" self->main();\n"); + state.write(" self->store_flat_outputs(flats);\n"); + state.write(" self->store_interp_outputs(interps, interp_stride);\n"); + state.write("}\n"); + + write!(state, "{}_vert() {{\n", state.name); + } + } + state.write(" set_uniform_1i_func = (SetUniform1iFunc)&set_uniform_1i;\n"); + state.write(" set_uniform_4fv_func = (SetUniform4fvFunc)&set_uniform_4fv;\n"); + state.write(" set_uniform_matrix4fv_func = (SetUniformMatrix4fvFunc)&set_uniform_matrix4fv;\n"); + match state.kind { + ShaderKind::Fragment => { + state.write(" init_batch_func = (InitBatchFunc)&bind_textures;\n"); + state.write(" init_primitive_func = (InitPrimitiveFunc)&read_flat_inputs;\n"); + state.write(" init_span_func = (InitSpanFunc)&read_interp_inputs;\n"); + state.write(" run_func = (RunFunc)&run;\n"); + state.write(" skip_func = (SkipFunc)&skip;\n"); + state.write(" use_discard_func = (UseDiscardFunc)&use_discard;\n"); + if state.has_draw_span_rgba8 { + state.write(" draw_span_RGBA8_func = (DrawSpanRGBA8Func)&draw_span_RGBA8;\n"); + } + if state.has_draw_span_r8 { + state.write(" draw_span_R8_func = (DrawSpanR8Func)&draw_span_R8;\n"); + } + } + ShaderKind::Vertex => { + state.write(" init_batch_func = (InitBatchFunc)&bind_textures;\n"); + state.write(" load_attribs_func = (LoadAttribsFunc)&load_attribs;\n"); + state.write(" run_func = (RunFunc)&run;\n"); + } + } + state.write("}\n"); +} + +pub fn define_global_consts(state: &mut OutputState, tu: &hir::TranslationUnit, part_name: &str) { + for i in tu { + match i { + hir::ExternalDeclaration::Declaration(hir::Declaration::InitDeclaratorList(ref d)) => { + let sym = state.hir.sym(d.head.name); + match &sym.decl { + hir::SymDecl::Global(hir::StorageClass::Const, ..) => { + let is_scalar = state.is_scalar.replace( + symbol_run_class(&sym.decl, state.vector_mask) == hir::RunClass::Scalar, + ); + if let Some(ref _array) = d.head.ty.array_sizes { + show_type(state, &d.head.ty); + } else { + if let Some(ty_def) = d.head.ty_def { + show_sym_decl(state, &ty_def); + } else { + show_type(state, &d.head.ty); + } + } + write!(state, " constexpr {}::{};\n", part_name, sym.name); + state.is_scalar.set(is_scalar); + } + _ => {} + } + } + _ => {} + } + } +} diff --git a/glsl-to-cxx/src/main.rs b/glsl-to-cxx/src/main.rs new file mode 100644 index 0000000000..e40262c84e --- /dev/null +++ b/glsl-to-cxx/src/main.rs @@ -0,0 +1,8 @@ +/* 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 glsl_to_cxx::translate; +fn main() { + println!("{}", translate(&mut std::env::args())); +} diff --git a/swgl/Cargo.toml b/swgl/Cargo.toml new file mode 100644 index 0000000000..55df40ccba --- /dev/null +++ b/swgl/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "swgl" +version = "0.1.0" +license = "MPL-2.0" +authors = ["The Mozilla Project Developers"] +build = "build.rs" +description = "Software OpenGL implementation for WebRender." + +[build-dependencies] +cc = "1.0.46" +glsl-to-cxx = { path = "../glsl-to-cxx" } + +[dependencies] +gleam = "0.10.0" diff --git a/swgl/README.md b/swgl/README.md new file mode 100644 index 0000000000..158a415793 --- /dev/null +++ b/swgl/README.md @@ -0,0 +1,4 @@ +swgl +======== + +Software OpenGL implementation for WebRender diff --git a/swgl/build.rs b/swgl/build.rs new file mode 100644 index 0000000000..9240af4812 --- /dev/null +++ b/swgl/build.rs @@ -0,0 +1,178 @@ +/* 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/. */ + +extern crate cc; +extern crate glsl_to_cxx; + +use std::collections::HashSet; +use std::fmt::Write; + +fn write_load_shader(shaders: &[&str]) { + let mut load_shader = String::new(); + for s in shaders { + let _ = write!(load_shader, "#include \"{}.h\"\n", s); + } + load_shader.push_str("ProgramLoader load_shader(const char* name) {\n"); + for s in shaders { + let _ = write!(load_shader, " if (!strcmp(name, \"{}\")) {{ return {}_program::loader; }}\n", s, s); + } + load_shader.push_str(" return nullptr;\n}\n"); + std::fs::write(std::env::var("OUT_DIR").unwrap() + "/load_shader.h", load_shader).unwrap(); +} + +fn process_imports(shader_dir: &str, shader: &str, included: &mut HashSet, output: &mut String) { + if !included.insert(shader.into()) { + return; + } + println!("cargo:rerun-if-changed={}/{}.glsl", shader_dir, shader); + let source = std::fs::read_to_string(format!("{}/{}.glsl", shader_dir, shader)).unwrap(); + for line in source.lines() { + if line.starts_with("#include ") { + let imports = line["#include ".len() ..].split(','); + for import in imports { + process_imports(shader_dir, import, included, output); + } + } else if line.starts_with("#version ") || line.starts_with("#extension ") { + // ignore + } else { + output.push_str(line); + output.push('\n'); + } + } +} + +fn translate_shader(shader: &str, shader_dir: &str) { + let mut imported = String::new(); + imported.push_str("#define SWGL 1\n"); + imported.push_str("#define WR_MAX_VERTEX_TEXTURE_WIDTH 1024U\n"); + let basename = if let Some(feature_start) = shader.find(char::is_uppercase) { + let feature_end = shader.rfind(char::is_uppercase).unwrap(); + let features = shader[feature_start .. feature_end + 1].split('.'); + for feature in features { + let _ = write!(imported, "#define WR_FEATURE_{}\n", feature); + } + &shader[0..feature_start] + } else { + shader + }; + + process_imports(shader_dir, basename, &mut HashSet::new(), &mut imported); + + let out_dir = std::env::var("OUT_DIR").unwrap(); + let imp_name = format!("{}/{}.c", out_dir, shader); + std::fs::write(&imp_name, imported).unwrap(); + + let mut build = cc::Build::new(); + if build.get_compiler().is_like_msvc() { + build.flag("/EP"); + } else { + build.flag("-xc").flag("-P"); + } + build.file(&imp_name); + let vs = build.clone() + .define("WR_VERTEX_SHADER", Some("1")) + .expand(); + let fs = build.clone() + .define("WR_FRAGMENT_SHADER", Some("1")) + .expand(); + let vs_name = format!("{}/{}.vert", out_dir, shader); + let fs_name = format!("{}/{}.frag", out_dir, shader); + std::fs::write(&vs_name, vs).unwrap(); + std::fs::write(&fs_name, fs).unwrap(); + + let mut args = vec![ + "glsl_to_cxx".to_string(), + vs_name, + fs_name, + ]; + let frag_include = format!("{}/{}.frag.h", shader_dir, shader); + if std::path::Path::new(&frag_include).exists() { + println!("cargo:rerun-if-changed={}/{}.frag.h", shader_dir, shader); + args.push(frag_include); + } + let result = glsl_to_cxx::translate(&mut args.into_iter()); + std::fs::write(format!("{}/{}.h", out_dir, shader), result).unwrap(); +} + +const WR_SHADERS: &'static [&'static str] = &[ + "brush_blendALPHA_PASS", + "brush_blend", + "brush_imageALPHA_PASS", + "brush_image", + "brush_imageREPETITION_ANTIALIASING_ALPHA_PASS", + "brush_imageREPETITION_ANTIALIASING", + "brush_linear_gradientALPHA_PASS", + "brush_linear_gradientDITHERING_ALPHA_PASS", + "brush_linear_gradientDITHERING", + "brush_linear_gradient", + "brush_mix_blendALPHA_PASS", + "brush_mix_blend", + "brush_opacityALPHA_PASS", + "brush_radial_gradientALPHA_PASS", + "brush_radial_gradientDITHERING_ALPHA_PASS", + "brush_radial_gradientDITHERING", + "brush_radial_gradient", + "brush_solidALPHA_PASS", + "brush_solid", + "brush_yuv_image", + "brush_yuv_imageTEXTURE_2D_YUV_NV12", + "brush_yuv_imageYUV", + "brush_yuv_imageYUV_INTERLEAVED", + "brush_yuv_imageYUV_NV12_ALPHA_PASS", + "brush_yuv_imageYUV_NV12", + "brush_yuv_imageYUV_PLANAR", + "composite", + "cs_blurALPHA_TARGET", + "cs_blurCOLOR_TARGET", + "cs_border_segment", + "cs_border_solid", + "cs_clip_box_shadow", + "cs_clip_image", + "cs_clip_rectangleFAST_PATH", + "cs_clip_rectangle", + "cs_gradient", + "cs_line_decoration", + "cs_scale", + "cs_svg_filter", + "debug_color", + "debug_font", + "ps_split_composite", + "ps_text_runDUAL_SOURCE_BLENDING", + "ps_text_runGLYPH_TRANSFORM", + "ps_text_runDUAL_SOURCE_BLENDING_GLYPH_TRANSFORM", + "ps_text_run", +]; + +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 { + translate_shader(shader, &shader_dir); + } + + write_load_shader(WR_SHADERS); + + println!("cargo:rerun-if-changed=src/gl_defs.h"); + println!("cargo:rerun-if-changed=src/glsl.h"); + println!("cargo:rerun-if-changed=src/program.h"); + println!("cargo:rerun-if-changed=src/texture.h"); + println!("cargo:rerun-if-changed=src/vector_type.h"); + println!("cargo:rerun-if-changed=src/gl.cc"); + cc::Build::new() + .cpp(true) + .file("src/gl.cc") + .flag("-std=c++14") + .flag("-UMOZILLA_CONFIG_H") + .flag("-fno-exceptions") + .flag("-fno-rtti") + .flag("-fno-math-errno") + .define("_GLIBCXX_USE_CXX11_ABI", Some("0")) + .include(shader_dir) + .include("src") + .include(std::env::var("OUT_DIR").unwrap()) + .compile("gl_cc"); +} diff --git a/swgl/src/gl.cc b/swgl/src/gl.cc new file mode 100644 index 0000000000..5c322fd8e4 --- /dev/null +++ b/swgl/src/gl.cc @@ -0,0 +1,3114 @@ +/* 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/. */ + +#include +#include +#include +#include +#include +#include + +#ifdef __MACH__ +# include +# include +#else +# include +#endif + +#ifdef NDEBUG +# define debugf(...) +#else +# define debugf(...) printf(__VA_ARGS__) +#endif + +#ifdef _WIN32 +# define ALWAYS_INLINE __forceinline +#else +# define ALWAYS_INLINE __attribute__((always_inline)) inline +#endif + +#define UNREACHABLE __builtin_unreachable() + +#define UNUSED __attribute__((unused)) + +#ifdef MOZILLA_CLIENT +# define IMPLICIT __attribute__((annotate("moz_implicit"))) +#else +# define IMPLICIT +#endif + +#include "gl_defs.h" +#include "glsl.h" +#include "program.h" + +using namespace glsl; + +struct IntRect { + int x; + int y; + int width; + int height; +}; + +struct VertexAttrib { + size_t size = 0; // in bytes + GLenum type = 0; + bool normalized = false; + GLsizei stride = 0; + GLuint offset = 0; + bool enabled = false; + GLuint divisor = 0; + int vertex_array = 0; + int vertex_buffer = 0; + char* buf = nullptr; // XXX: this can easily dangle + size_t buf_size = 0; // this will let us bounds check +}; + +static int bytes_for_internal_format(GLenum internal_format) { + switch (internal_format) { + case GL_RGBA32F: + return 4 * 4; + case GL_RGBA32I: + return 4 * 4; + case GL_RGBA8: + case GL_BGRA8: + case GL_RGBA: + return 4; + case GL_R8: + case GL_RED: + return 1; + case GL_DEPTH_COMPONENT: + case GL_DEPTH_COMPONENT16: + return 2; + case GL_DEPTH_COMPONENT24: + case GL_DEPTH_COMPONENT32: + return 4; + default: + debugf("internal format: %x\n", internal_format); + assert(0); + return 0; + } +} + +static inline int aligned_stride(int row_bytes) { return (row_bytes + 3) & ~3; } + +static TextureFormat gl_format_to_texture_format(int type) { + switch (type) { + case GL_RGBA32F: + return TextureFormat::RGBA32F; + case GL_RGBA32I: + return TextureFormat::RGBA32I; + case GL_RGBA8: + return TextureFormat::RGBA8; + case GL_R8: + return TextureFormat::R8; + default: + assert(0); + return TextureFormat::RGBA8; + } +} + +struct Query { + uint64_t value = 0; +}; + +struct Buffer { + char* buf = nullptr; + size_t size = 0; + + bool allocate(size_t new_size) { + if (new_size != size) { + char* new_buf = (char*)realloc(buf, new_size); + assert(new_buf); + if (new_buf) { + buf = new_buf; + size = new_size; + return true; + } + cleanup(); + } + return false; + } + + void cleanup() { + if (buf) { + free(buf); + buf = nullptr; + size = 0; + } + } + + ~Buffer() { cleanup(); } +}; + +struct Framebuffer { + GLuint color_attachment = 0; + GLint layer = 0; + GLuint depth_attachment = 0; +}; + +struct Renderbuffer { + GLuint texture = 0; + + ~Renderbuffer(); +}; + +TextureFilter gl_filter_to_texture_filter(int type) { + switch (type) { + case GL_NEAREST: + return TextureFilter::NEAREST; + case GL_NEAREST_MIPMAP_LINEAR: + return TextureFilter::NEAREST; + case GL_NEAREST_MIPMAP_NEAREST: + return TextureFilter::NEAREST; + case GL_LINEAR: + return TextureFilter::LINEAR; + case GL_LINEAR_MIPMAP_LINEAR: + return TextureFilter::LINEAR; + case GL_LINEAR_MIPMAP_NEAREST: + return TextureFilter::LINEAR; + default: + assert(0); + return TextureFilter::NEAREST; + } +} + +struct Texture { + int levels = 0; + GLenum internal_format = 0; + int width = 0; + int height = 0; + int depth = 0; + char* buf = nullptr; + size_t buf_size = 0; + GLenum min_filter = GL_NEAREST; + GLenum mag_filter = GL_LINEAR; + + enum FLAGS { + SHOULD_FREE = 1 << 1, + }; + int flags = SHOULD_FREE; + bool should_free() const { return bool(flags & SHOULD_FREE); } + + void set_flag(int flag, bool val) { + if (val) { + flags |= flag; + } else { + flags &= ~flag; + } + } + void set_should_free(bool val) { set_flag(SHOULD_FREE, val); } + + int delay_clear = 0; + uint32_t clear_val = 0; + uint32_t* cleared_rows = nullptr; + + void enable_delayed_clear(uint32_t val) { + delay_clear = height; + clear_val = val; + if (!cleared_rows) { + cleared_rows = new uint32_t[(height + 31) / 32]; + } + memset(cleared_rows, 0, ((height + 31) / 32) * sizeof(uint32_t)); + if (height & 31) { + cleared_rows[height / 32] = ~0U << (height & 31); + } + } + + void disable_delayed_clear() { + if (cleared_rows) { + delete[] cleared_rows; + cleared_rows = nullptr; + delay_clear = 0; + } + } + + int bpp() const { return bytes_for_internal_format(internal_format); } + + size_t stride(int b = 0, int min_width = 0) const { + return aligned_stride((b ? b : bpp()) * max(width, min_width)); + } + + size_t layer_stride(int b = 0, int min_width = 0, int min_height = 0) const { + return stride(b ? b : bpp(), min_width) * max(height, min_height); + } + + bool allocate(bool force = false, int min_width = 0, int min_height = 0) { + if ((!buf || force) && should_free()) { + size_t size = + layer_stride(bpp(), min_width, min_height) * max(depth, 1) * levels; + if (!buf || size > buf_size) { + char* new_buf = (char*)realloc(buf, size + sizeof(Float)); + assert(new_buf); + if (new_buf) { + buf = new_buf; + buf_size = size; + return true; + } + cleanup(); + } + } + return false; + } + + void cleanup() { + if (buf && should_free()) { + free(buf); + buf = nullptr; + buf_size = 0; + } + disable_delayed_clear(); + } + + ~Texture() { cleanup(); } +}; + +#define MAX_ATTRIBS 16 +#define NULL_ATTRIB 15 +struct VertexArray { + VertexAttrib attribs[MAX_ATTRIBS]; + int max_attrib = -1; + + void validate(); +}; + +struct Shader { + GLenum type = 0; + ProgramLoader loader = nullptr; +}; + +struct Program { + ProgramImpl* impl = nullptr; + VertexShaderImpl* vert_impl = nullptr; + FragmentShaderImpl* frag_impl = nullptr; + bool deleted = false; + + ~Program() { + delete impl; + delete vert_impl; + delete frag_impl; + } +}; + +// for GL defines to fully expand +#define CONCAT_KEY(prefix, x, y, z, w, ...) prefix##x##y##z##w +#define BLEND_KEY(...) CONCAT_KEY(BLEND_, __VA_ARGS__, 0, 0) +#define FOR_EACH_BLEND_KEY(macro) \ + macro(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE) \ + macro(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, 0, 0) \ + macro(GL_ZERO, GL_ONE_MINUS_SRC_COLOR, 0, 0) \ + macro(GL_ZERO, GL_ONE_MINUS_SRC_COLOR, GL_ZERO, GL_ONE) \ + macro(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA, 0, 0) macro( \ + GL_ZERO, GL_SRC_COLOR, 0, 0) macro(GL_ONE, GL_ONE, 0, 0) \ + macro(GL_ONE, GL_ONE, GL_ONE, GL_ONE_MINUS_SRC_ALPHA) \ + macro(GL_ONE, GL_ZERO, 0, 0) macro( \ + GL_ONE_MINUS_DST_ALPHA, GL_ONE, GL_ZERO, GL_ONE) \ + macro(GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_COLOR, \ + 0, 0) \ + macro(GL_ONE, GL_ONE_MINUS_SRC1_COLOR, 0, 0) + +#define DEFINE_BLEND_KEY(...) BLEND_KEY(__VA_ARGS__), +enum BlendKey : uint8_t { + BLEND_KEY_NONE = 0, + FOR_EACH_BLEND_KEY(DEFINE_BLEND_KEY) +}; + +const size_t MAX_TEXTURE_UNITS = 16; + +template +static inline bool unlink(T& binding, T n) { + if (binding == n) { + binding = 0; + return true; + } + return false; +} + +template +struct ObjectStore { + O** objects = nullptr; + size_t size = 0; + // reserve object 0 as null + size_t first_free = 1; + O invalid; + + ~ObjectStore() { + if (objects) { + for (size_t i = 0; i < size; i++) delete objects[i]; + free(objects); + } + } + + bool grow(size_t i) { + size_t new_size = size ? size : 8; + while (new_size <= i) new_size += new_size / 2; + O** new_objects = (O**)realloc(objects, new_size * sizeof(O*)); + assert(new_objects); + if (!new_objects) return false; + while (size < new_size) new_objects[size++] = nullptr; + objects = new_objects; + return true; + } + + void insert(size_t i, const O& o) { + if (i >= size && !grow(i)) return; + if (!objects[i]) objects[i] = new O(o); + } + + size_t next_free() { + size_t i = first_free; + while (i < size && objects[i]) i++; + first_free = i; + return i; + } + + size_t insert(const O& o = O()) { + size_t i = next_free(); + insert(i, o); + return i; + } + + O& operator[](size_t i) { + insert(i, O()); + return i < size ? *objects[i] : invalid; + } + + O* find(size_t i) const { return i < size ? objects[i] : nullptr; } + + bool erase(size_t i) { + if (i < size && objects[i]) { + delete objects[i]; + objects[i] = nullptr; + if (i < first_free) first_free = i; + return true; + } + return false; + } + + O** begin() const { return objects; } + O** end() const { return &objects[size]; } +}; + +struct Context { + ObjectStore queries; + ObjectStore buffers; + ObjectStore textures; + ObjectStore vertex_arrays; + ObjectStore framebuffers; + ObjectStore renderbuffers; + ObjectStore shaders; + ObjectStore programs; + + IntRect viewport = {0, 0, 0, 0}; + + bool blend = false; + GLenum blendfunc_srgb = GL_ONE; + GLenum blendfunc_drgb = GL_ZERO; + GLenum blendfunc_sa = GL_ONE; + GLenum blendfunc_da = GL_ZERO; + GLenum blend_equation = GL_FUNC_ADD; + V8 blendcolor = 0; + BlendKey blend_key = BLEND_KEY_NONE; + + bool depthtest = false; + bool depthmask = true; + GLenum depthfunc = GL_LESS; + + bool scissortest = false; + IntRect scissor = {0, 0, 0, 0}; + + uint32_t clearcolor = 0; + GLdouble cleardepth = 1; + + int unpack_row_length = 0; + + int shaded_rows = 0; + int shaded_pixels = 0; + + struct TextureUnit { + GLuint texture_2d_binding = 0; + GLuint texture_3d_binding = 0; + GLuint texture_2d_array_binding = 0; + + void unlink(GLuint n) { + ::unlink(texture_2d_binding, n); + ::unlink(texture_3d_binding, n); + ::unlink(texture_2d_array_binding, n); + } + }; + TextureUnit texture_units[MAX_TEXTURE_UNITS]; + int active_texture_unit = 0; + + GLuint current_program = 0; + + GLuint current_vertex_array = 0; + bool validate_vertex_array = true; + + GLuint pixel_pack_buffer_binding = 0; + GLuint pixel_unpack_buffer_binding = 0; + GLuint array_buffer_binding = 0; + GLuint element_array_buffer_binding = 0; + GLuint time_elapsed_query = 0; + GLuint samples_passed_query = 0; + GLuint renderbuffer_binding = 0; + GLuint draw_framebuffer_binding = 0; + GLuint read_framebuffer_binding = 0; + GLuint unknown_binding = 0; + + GLuint& get_binding(GLenum name) { + switch (name) { + case GL_PIXEL_PACK_BUFFER: + return pixel_pack_buffer_binding; + case GL_PIXEL_UNPACK_BUFFER: + return pixel_unpack_buffer_binding; + case GL_ARRAY_BUFFER: + return array_buffer_binding; + case GL_ELEMENT_ARRAY_BUFFER: + return element_array_buffer_binding; + case GL_TEXTURE_2D: + return texture_units[active_texture_unit].texture_2d_binding; + case GL_TEXTURE_2D_ARRAY: + return texture_units[active_texture_unit].texture_2d_array_binding; + case GL_TEXTURE_3D: + return texture_units[active_texture_unit].texture_3d_binding; + case GL_TIME_ELAPSED: + return time_elapsed_query; + case GL_SAMPLES_PASSED: + return samples_passed_query; + case GL_RENDERBUFFER: + return renderbuffer_binding; + case GL_DRAW_FRAMEBUFFER: + return draw_framebuffer_binding; + case GL_READ_FRAMEBUFFER: + return read_framebuffer_binding; + default: + debugf("unknown binding %x\n", name); + assert(false); + return unknown_binding; + } + } +}; +static Context* ctx = nullptr; +static ProgramImpl* program_impl = nullptr; +static VertexShaderImpl* vertex_shader = nullptr; +static FragmentShaderImpl* fragment_shader = nullptr; +static BlendKey blend_key = BLEND_KEY_NONE; + +static void prepare_texture(Texture& t, const IntRect* skip = nullptr); + +template +static inline void init_depth(S* s, Texture& t) { + s->depth = t.depth; + s->height_stride = s->stride * t.height; +} + +template +static inline void init_filter(S* s, Texture& t) { + s->filter = gl_filter_to_texture_filter(t.mag_filter); +} + +template +static inline void init_sampler(S* s, Texture& t) { + prepare_texture(t); + s->width = t.width; + s->height = t.height; + int bpp = t.bpp(); + s->stride = t.stride(bpp); + if (bpp >= 4) s->stride /= 4; + // Use uint32_t* for easier sampling, but need to cast to uint8_t* for formats + // with bpp < 4. + s->buf = (uint32_t*)t.buf; + s->format = gl_format_to_texture_format(t.internal_format); +} + +template +S* lookup_sampler(S* s, int texture) { + Texture& t = ctx->textures[ctx->texture_units[texture].texture_2d_binding]; + if (!t.buf) { + *s = S(); + } else { + init_sampler(s, t); + init_filter(s, t); + } + return s; +} + +template +S* lookup_isampler(S* s, int texture) { + Texture& t = ctx->textures[ctx->texture_units[texture].texture_2d_binding]; + if (!t.buf) { + *s = S(); + } else { + init_sampler(s, t); + } + return s; +} + +template +S* lookup_sampler_array(S* s, int texture) { + Texture& t = + ctx->textures[ctx->texture_units[texture].texture_2d_array_binding]; + if (!t.buf) { + *s = S(); + } else { + init_sampler(s, t); + init_depth(s, t); + init_filter(s, t); + } + return s; +} + +int bytes_per_type(GLenum type) { + switch (type) { + case GL_INT: + return 4; + case GL_FLOAT: + return 4; + case GL_UNSIGNED_SHORT: + return 2; + case GL_UNSIGNED_BYTE: + return 1; + default: + assert(0); + return 0; + } +} + +template +static inline S load_attrib_scalar(const char* src, size_t size, GLenum type, + bool normalized) { + if (sizeof(S) <= size) { + return *reinterpret_cast(src); + } + S scalar = {0}; + if (type == GL_UNSIGNED_SHORT) { + if (normalized) { + for (size_t i = 0; i < size / sizeof(uint16_t); i++) { + typename ElementType::ty x = + reinterpret_cast(src)[i]; + put_nth_component(scalar, i, x * (1.0f / 0xFFFF)); + } + } else { + for (size_t i = 0; i < size / sizeof(uint16_t); i++) { + typename ElementType::ty x = + reinterpret_cast(src)[i]; + put_nth_component(scalar, i, x); + } + } + } else { + assert(sizeof(typename ElementType::ty) == bytes_per_type(type)); + memcpy(&scalar, src, size); + } + return scalar; +} + +template +void load_attrib(T& attrib, VertexAttrib& va, uint16_t* indices, int start, + int instance, int count) { + typedef decltype(force_scalar(attrib)) scalar_type; + if (!va.enabled) { + attrib = T(scalar_type{0}); + } else if (va.divisor == 1) { + char* src = (char*)va.buf + va.stride * instance + va.offset; + assert(src + va.size <= va.buf + va.buf_size); + attrib = T( + load_attrib_scalar(src, va.size, va.type, va.normalized)); + } else if (va.divisor == 0) { + if (!indices) return; + assert(sizeof(typename ElementType::ty) == bytes_per_type(va.type)); + assert(count == 3 || count == 4); + attrib = (T){ + load_attrib_scalar( + (char*)va.buf + va.stride * indices[start + 0] + va.offset, va.size, + va.type, va.normalized), + load_attrib_scalar( + (char*)va.buf + va.stride * indices[start + 1] + va.offset, va.size, + va.type, va.normalized), + load_attrib_scalar( + (char*)va.buf + va.stride * indices[start + 2] + va.offset, va.size, + va.type, va.normalized), + load_attrib_scalar( + (char*)va.buf + va.stride * indices[start + (count > 3 ? 3 : 2)] + + va.offset, + va.size, va.type, va.normalized), + }; + } else { + assert(false); + } +} + +template +void load_flat_attrib(T& attrib, VertexAttrib& va, uint16_t* indices, int start, + int instance, int count) { + typedef decltype(force_scalar(attrib)) scalar_type; + if (!va.enabled) { + attrib = T{0}; + return; + } + char* src = nullptr; + if (va.divisor == 1) { + src = (char*)va.buf + va.stride * instance + va.offset; + } else if (va.divisor == 0) { + if (!indices) return; + src = (char*)va.buf + va.stride * indices[start] + va.offset; + } else { + assert(false); + } + assert(src + va.size <= va.buf + va.buf_size); + attrib = + T(load_attrib_scalar(src, va.size, va.type, va.normalized)); +} + +void setup_program(GLuint program) { + if (!program) { + program_impl = nullptr; + vertex_shader = nullptr; + fragment_shader = nullptr; + return; + } + Program& p = ctx->programs[program]; + assert(p.impl); + assert(p.vert_impl); + assert(p.frag_impl); + program_impl = p.impl; + vertex_shader = p.vert_impl; + fragment_shader = p.frag_impl; +} + +extern ProgramLoader load_shader(const char* name); + +extern "C" { + +void UseProgram(GLuint program) { + if (ctx->current_program && program != ctx->current_program) { + auto* p = ctx->programs.find(ctx->current_program); + if (p && p->deleted) { + ctx->programs.erase(ctx->current_program); + } + } + ctx->current_program = program; + setup_program(program); +} + +void SetViewport(GLint x, GLint y, GLsizei width, GLsizei height) { + ctx->viewport.x = x; + ctx->viewport.y = y; + ctx->viewport.width = width; + ctx->viewport.height = height; +} + +void Enable(GLenum cap) { + switch (cap) { + case GL_BLEND: + ctx->blend = true; + blend_key = ctx->blend_key; + break; + case GL_DEPTH_TEST: + ctx->depthtest = true; + break; + case GL_SCISSOR_TEST: + ctx->scissortest = true; + break; + } +} + +void Disable(GLenum cap) { + switch (cap) { + case GL_BLEND: + ctx->blend = false; + blend_key = BLEND_KEY_NONE; + break; + case GL_DEPTH_TEST: + ctx->depthtest = false; + break; + case GL_SCISSOR_TEST: + ctx->scissortest = false; + break; + } +} + +GLenum GetError() { return GL_NO_ERROR; } + +static const char* const extensions[] = { + "GL_ARB_blend_func_extended", "GL_ARB_copy_image", + "GL_ARB_draw_instanced", "GL_ARB_explicit_attrib_location", + "GL_ARB_instanced_arrays", "GL_ARB_invalidate_subdata", + "GL_ARB_texture_storage", "GL_EXT_timer_query", +}; + +void GetIntegerv(GLenum pname, GLint* params) { + assert(params); + switch (pname) { + case GL_MAX_TEXTURE_UNITS: + case GL_MAX_TEXTURE_IMAGE_UNITS: + params[0] = MAX_TEXTURE_UNITS; + break; + case GL_MAX_TEXTURE_SIZE: + params[0] = 1 << 15; + break; + case GL_MAX_ARRAY_TEXTURE_LAYERS: + params[0] = 1 << 15; + break; + case GL_READ_FRAMEBUFFER_BINDING: + params[0] = ctx->read_framebuffer_binding; + break; + case GL_DRAW_FRAMEBUFFER_BINDING: + params[0] = ctx->draw_framebuffer_binding; + break; + case GL_PIXEL_PACK_BUFFER_BINDING: + params[0] = ctx->pixel_pack_buffer_binding; + break; + case GL_PIXEL_UNPACK_BUFFER_BINDING: + params[0] = ctx->pixel_unpack_buffer_binding; + break; + case GL_NUM_EXTENSIONS: + params[0] = sizeof(extensions) / sizeof(extensions[0]); + break; + default: + debugf("unhandled glGetIntegerv parameter %x\n", pname); + assert(false); + } +} + +void GetBooleanv(GLenum pname, GLboolean* params) { + assert(params); + switch (pname) { + case GL_DEPTH_WRITEMASK: + params[0] = ctx->depthmask; + break; + default: + debugf("unhandled glGetBooleanv parameter %x\n", pname); + assert(false); + } +} + +const char* GetString(GLenum name) { + switch (name) { + case GL_VENDOR: + return "Mozilla Gfx"; + case GL_RENDERER: + return "Software WebRender"; + case GL_VERSION: + return "3.2"; + default: + debugf("unhandled glGetString parameter %x\n", name); + assert(false); + return nullptr; + } +} + +const char* GetStringi(GLenum name, GLuint index) { + switch (name) { + case GL_EXTENSIONS: + if (index >= sizeof(extensions) / sizeof(extensions[0])) { + return nullptr; + } + return extensions[index]; + default: + debugf("unhandled glGetStringi parameter %x\n", name); + assert(false); + return nullptr; + } +} + +GLenum remap_blendfunc(GLenum rgb, GLenum a) { + switch (a) { + case GL_SRC_ALPHA: + if (rgb == GL_SRC_COLOR) a = GL_SRC_COLOR; + break; + case GL_ONE_MINUS_SRC_ALPHA: + if (rgb == GL_ONE_MINUS_SRC_COLOR) a = GL_ONE_MINUS_SRC_COLOR; + break; + case GL_DST_ALPHA: + if (rgb == GL_DST_COLOR) a = GL_DST_COLOR; + break; + case GL_ONE_MINUS_DST_ALPHA: + if (rgb == GL_ONE_MINUS_DST_COLOR) a = GL_ONE_MINUS_DST_COLOR; + break; + case GL_CONSTANT_ALPHA: + if (rgb == GL_CONSTANT_COLOR) a = GL_CONSTANT_COLOR; + break; + case GL_ONE_MINUS_CONSTANT_ALPHA: + if (rgb == GL_ONE_MINUS_CONSTANT_COLOR) a = GL_ONE_MINUS_CONSTANT_COLOR; + break; + case GL_SRC_COLOR: + if (rgb == GL_SRC_ALPHA) a = GL_SRC_ALPHA; + break; + case GL_ONE_MINUS_SRC_COLOR: + if (rgb == GL_ONE_MINUS_SRC_ALPHA) a = GL_ONE_MINUS_SRC_ALPHA; + break; + case GL_DST_COLOR: + if (rgb == GL_DST_ALPHA) a = GL_DST_ALPHA; + break; + case GL_ONE_MINUS_DST_COLOR: + if (rgb == GL_ONE_MINUS_DST_ALPHA) a = GL_ONE_MINUS_DST_ALPHA; + break; + case GL_CONSTANT_COLOR: + if (rgb == GL_CONSTANT_ALPHA) a = GL_CONSTANT_ALPHA; + break; + case GL_ONE_MINUS_CONSTANT_COLOR: + if (rgb == GL_ONE_MINUS_CONSTANT_ALPHA) a = GL_ONE_MINUS_CONSTANT_ALPHA; + break; + case GL_SRC1_ALPHA: + if (rgb == GL_SRC1_COLOR) a = GL_SRC1_COLOR; + break; + case GL_ONE_MINUS_SRC1_ALPHA: + if (rgb == GL_ONE_MINUS_SRC1_COLOR) a = GL_ONE_MINUS_SRC1_COLOR; + break; + case GL_SRC1_COLOR: + if (rgb == GL_SRC1_ALPHA) a = GL_SRC1_ALPHA; + break; + case GL_ONE_MINUS_SRC1_COLOR: + if (rgb == GL_ONE_MINUS_SRC1_ALPHA) a = GL_ONE_MINUS_SRC1_ALPHA; + break; + } + return a; +} + +void BlendFunc(GLenum srgb, GLenum drgb, GLenum sa, GLenum da) { + ctx->blendfunc_srgb = srgb; + ctx->blendfunc_drgb = drgb; + sa = remap_blendfunc(srgb, sa); + da = remap_blendfunc(drgb, da); + ctx->blendfunc_sa = sa; + ctx->blendfunc_da = da; + +#define HASH_BLEND_KEY(x, y, z, w) ((x << 4) | (y) | (z << 24) | (w << 20)) + int hash = HASH_BLEND_KEY(srgb, drgb, 0, 0); + if (srgb != sa || drgb != da) hash |= HASH_BLEND_KEY(0, 0, sa, da); + switch (hash) { +#define MAP_BLEND_KEY(...) \ + case HASH_BLEND_KEY(__VA_ARGS__): \ + ctx->blend_key = BLEND_KEY(__VA_ARGS__); \ + break; + FOR_EACH_BLEND_KEY(MAP_BLEND_KEY) + default: + debugf("blendfunc: %x, %x, separate: %x, %x\n", srgb, drgb, sa, da); + assert(false); + break; + } + + if (ctx->blend) { + blend_key = ctx->blend_key; + } +} + +void BlendColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) { + I32 c = roundto((Float){b, g, r, a}, 255.49f); + ctx->blendcolor = CONVERT(c, U16).xyzwxyzw; +} + +void BlendEquation(GLenum mode) { + assert(mode == GL_FUNC_ADD); + ctx->blend_equation = mode; +} + +void DepthMask(GLboolean flag) { ctx->depthmask = flag; } + +void DepthFunc(GLenum func) { + switch (func) { + case GL_LESS: + case GL_LEQUAL: + break; + default: + assert(false); + } + ctx->depthfunc = func; +} + +void SetScissor(GLint x, GLint y, GLsizei width, GLsizei height) { + ctx->scissor.x = x; + ctx->scissor.y = y; + ctx->scissor.width = width; + ctx->scissor.height = height; +} + +void ClearColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) { + I32 c = roundto((Float){b, g, r, a}, 255.49f); + ctx->clearcolor = bit_cast(CONVERT(c, U8)); +} + +void ClearDepth(GLdouble depth) { ctx->cleardepth = depth; } + +void ActiveTexture(GLenum texture) { + assert(texture >= GL_TEXTURE0); + assert(texture < GL_TEXTURE0 + MAX_TEXTURE_UNITS); + ctx->active_texture_unit = + clamp(int(texture - GL_TEXTURE0), 0, int(MAX_TEXTURE_UNITS - 1)); +} + +void GenQueries(GLsizei n, GLuint* result) { + for (int i = 0; i < n; i++) { + Query q; + result[i] = ctx->queries.insert(q); + } +} + +void DeleteQuery(GLuint n) { + if (n && ctx->queries.erase(n)) { + unlink(ctx->time_elapsed_query, n); + unlink(ctx->samples_passed_query, n); + } +} + +void GenBuffers(int n, GLuint* result) { + for (int i = 0; i < n; i++) { + Buffer b; + result[i] = ctx->buffers.insert(b); + } +} + +void DeleteBuffer(GLuint n) { + if (n && ctx->buffers.erase(n)) { + unlink(ctx->pixel_pack_buffer_binding, n); + unlink(ctx->pixel_unpack_buffer_binding, n); + unlink(ctx->array_buffer_binding, n); + unlink(ctx->element_array_buffer_binding, n); + } +} + +void GenVertexArrays(int n, GLuint* result) { + for (int i = 0; i < n; i++) { + VertexArray v; + result[i] = ctx->vertex_arrays.insert(v); + } +} + +void DeleteVertexArray(GLuint n) { + if (n && ctx->vertex_arrays.erase(n)) { + unlink(ctx->current_vertex_array, n); + } +} + +GLuint CreateShader(GLenum type) { + Shader s; + s.type = type; + return ctx->shaders.insert(s); +} + +void ShaderSourceByName(GLuint shader, char* name) { + Shader& s = ctx->shaders[shader]; + s.loader = load_shader(name); + if (!s.loader) { + debugf("unknown shader %s\n", name); + } +} + +void AttachShader(GLuint program, GLuint shader) { + Program& p = ctx->programs[program]; + Shader& s = ctx->shaders[shader]; + if (s.type == GL_VERTEX_SHADER) { + if (!p.impl && s.loader) p.impl = s.loader(); + } else if (s.type == GL_FRAGMENT_SHADER) { + if (!p.impl && s.loader) p.impl = s.loader(); + } else { + assert(0); + } +} + +void DeleteShader(GLuint n) { + if (n) ctx->shaders.erase(n); +} + +GLuint CreateProgram() { + Program p; + return ctx->programs.insert(p); +} + +void DeleteProgram(GLuint n) { + if (!n) return; + if (ctx->current_program == n) { + if (auto* p = ctx->programs.find(n)) { + p->deleted = true; + } + } else { + ctx->programs.erase(n); + } +} + +void LinkProgram(GLuint program) { + Program& p = ctx->programs[program]; + assert(p.impl); + if (!p.vert_impl) p.vert_impl = p.impl->get_vertex_shader(); + if (!p.frag_impl) p.frag_impl = p.impl->get_fragment_shader(); +} + +void BindAttribLocation(GLuint program, GLuint index, char* name) { + Program& p = ctx->programs[program]; + assert(p.impl); + p.impl->bind_attrib(name, index); +} + +GLint GetAttribLocation(GLuint program, char* name) { + Program& p = ctx->programs[program]; + assert(p.impl); + return p.impl->get_attrib(name); +} + +GLint GetUniformLocation(GLuint program, char* name) { + Program& p = ctx->programs[program]; + assert(p.impl); + GLint loc = p.impl->get_uniform(name); + // debugf("location: %d\n", loc); + return loc; +} + +static uint64_t get_time_value() { +#ifdef __MACH__ + return mach_absolute_time(); +#elif defined(_WIN32) + return uint64_t(clock()) * (1000000000ULL / CLOCKS_PER_SEC); +#else + return ({ + struct timespec tp; + clock_gettime(CLOCK_MONOTONIC, &tp); + tp.tv_sec * 1000000000ULL + tp.tv_nsec; + }); +#endif +} + +void BeginQuery(GLenum target, GLuint id) { + ctx->get_binding(target) = id; + Query& q = ctx->queries[id]; + switch (target) { + case GL_SAMPLES_PASSED: + q.value = 0; + break; + case GL_TIME_ELAPSED: + q.value = get_time_value(); + break; + default: + debugf("unknown query target %x for query %d\n", target, id); + assert(false); + } +} + +void EndQuery(GLenum target) { + Query& q = ctx->queries[ctx->get_binding(target)]; + switch (target) { + case GL_SAMPLES_PASSED: + break; + case GL_TIME_ELAPSED: + q.value = get_time_value() - q.value; + break; + default: + debugf("unknown query target %x\n", target); + assert(false); + } + ctx->get_binding(target) = 0; +} + +void GetQueryObjectui64v(GLuint id, GLenum pname, GLuint64* params) { + Query& q = ctx->queries[id]; + switch (pname) { + case GL_QUERY_RESULT: + assert(params); + params[0] = q.value; + break; + default: + assert(false); + } +} + +void BindVertexArray(GLuint vertex_array) { + if (vertex_array != ctx->current_vertex_array) { + ctx->validate_vertex_array = true; + } + ctx->current_vertex_array = vertex_array; +} + +void BindTexture(GLenum target, GLuint texture) { + ctx->get_binding(target) = texture; +} + +void BindBuffer(GLenum target, GLuint buffer) { + ctx->get_binding(target) = buffer; +} + +void BindFramebuffer(GLenum target, GLuint fb) { + if (target == GL_FRAMEBUFFER) { + ctx->read_framebuffer_binding = fb; + ctx->draw_framebuffer_binding = fb; + } else { + assert(target == GL_READ_FRAMEBUFFER || target == GL_DRAW_FRAMEBUFFER); + ctx->get_binding(target) = fb; + } +} + +void BindRenderbuffer(GLenum target, GLuint rb) { + ctx->get_binding(target) = rb; +} + +void PixelStorei(GLenum name, GLint param) { + if (name == GL_UNPACK_ALIGNMENT) { + assert(param == 1); + } else if (name == GL_UNPACK_ROW_LENGTH) { + ctx->unpack_row_length = param; + } +} + +static GLenum remap_internal_format(GLenum format) { + switch (format) { + case GL_DEPTH_COMPONENT: + return GL_DEPTH_COMPONENT16; + case GL_RGBA: + return GL_RGBA8; + case GL_RED: + return GL_R8; + default: + return format; + } +} + +void TexStorage3D(GLenum target, GLint levels, GLenum internal_format, + GLsizei width, GLsizei height, GLsizei depth) { + Texture& t = ctx->textures[ctx->get_binding(target)]; + internal_format = remap_internal_format(internal_format); + bool changed = false; + if (t.width != width || t.height != height || t.depth != depth || + t.levels != levels || t.internal_format != internal_format) { + changed = true; + t.levels = levels; + t.internal_format = internal_format; + t.width = width; + t.height = height; + t.depth = depth; + } + t.disable_delayed_clear(); + t.allocate(changed); +} + +static void set_tex_storage(Texture& t, GLint levels, GLenum internal_format, + GLsizei width, GLsizei height, + bool should_free = true, void* buf = nullptr, + GLsizei min_width = 0, GLsizei min_height = 0) { + internal_format = remap_internal_format(internal_format); + bool changed = false; + if (t.width != width || t.height != height || t.levels != levels || + t.internal_format != internal_format) { + changed = true; + t.levels = levels; + t.internal_format = internal_format; + t.width = width; + t.height = height; + } + if (t.should_free() != should_free || buf != nullptr) { + if (t.should_free()) { + t.cleanup(); + } + t.set_should_free(should_free); + t.buf = (char*)buf; + t.buf_size = 0; + } + t.disable_delayed_clear(); + t.allocate(changed, min_width, min_height); +} + +void TexStorage2D(GLenum target, GLint levels, GLenum internal_format, + GLsizei width, GLsizei height) { + Texture& t = ctx->textures[ctx->get_binding(target)]; + set_tex_storage(t, levels, internal_format, width, height); +} + +GLenum internal_format_for_data(GLenum format, GLenum ty) { + if (format == GL_RED && ty == GL_UNSIGNED_BYTE) { + return GL_R8; + } else if ((format == GL_RGBA || format == GL_BGRA) && + ty == GL_UNSIGNED_BYTE) { + return GL_RGBA8; + } else if (format == GL_RGBA && ty == GL_FLOAT) { + return GL_RGBA32F; + } else if (format == GL_RGBA_INTEGER && ty == GL_INT) { + return GL_RGBA32I; + } else { + debugf("unknown internal format for format %x, type %x\n", format, ty); + assert(false); + return 0; + } +} + +static inline void copy_bgra8_to_rgba8(uint32_t* dest, uint32_t* src, + int width) { + for (; width >= 4; width -= 4, dest += 4, src += 4) { + U32 p = unaligned_load(src); + U32 rb = p & 0x00FF00FF; + unaligned_store(dest, (p & 0xFF00FF00) | (rb << 16) | (rb >> 16)); + } + for (; width > 0; width--, dest++, src++) { + uint32_t p = *src; + uint32_t rb = p & 0x00FF00FF; + *dest = (p & 0xFF00FF00) | (rb << 16) | (rb >> 16); + } +} + +static Buffer* get_pixel_pack_buffer() { + return ctx->pixel_pack_buffer_binding + ? &ctx->buffers[ctx->pixel_pack_buffer_binding] + : nullptr; +} + +static void* get_pixel_pack_buffer_data(void* data) { + if (Buffer* b = get_pixel_pack_buffer()) { + return b->buf ? b->buf + (size_t)data : nullptr; + } + return data; +} + +static Buffer* get_pixel_unpack_buffer() { + return ctx->pixel_unpack_buffer_binding + ? &ctx->buffers[ctx->pixel_unpack_buffer_binding] + : nullptr; +} + +static void* get_pixel_unpack_buffer_data(void* data) { + if (Buffer* b = get_pixel_unpack_buffer()) { + return b->buf ? b->buf + (size_t)data : nullptr; + } + return data; +} + +void TexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, + GLsizei width, GLsizei height, GLenum format, GLenum ty, + void* data) { + data = get_pixel_unpack_buffer_data(data); + if (!data) return; + Texture& t = ctx->textures[ctx->get_binding(target)]; + IntRect skip = {xoffset, yoffset, width, height}; + prepare_texture(t, &skip); + assert(xoffset + width <= t.width); + assert(yoffset + height <= t.height); + assert(ctx->unpack_row_length == 0 || ctx->unpack_row_length >= width); + GLsizei row_length = + ctx->unpack_row_length != 0 ? ctx->unpack_row_length : width; + assert(t.internal_format == internal_format_for_data(format, ty)); + int bpp = t.bpp(); + if (!bpp || !t.buf) return; + size_t dest_stride = t.stride(bpp); + char* dest = t.buf + yoffset * dest_stride + xoffset * bpp; + char* src = (char*)data; + for (int y = 0; y < height; y++) { + if (t.internal_format == GL_RGBA8 && format != GL_BGRA) { + copy_bgra8_to_rgba8((uint32_t*)dest, (uint32_t*)src, width); + } else { + memcpy(dest, src, width * bpp); + } + dest += dest_stride; + src += row_length * bpp; + } +} + +void TexImage2D(GLenum target, GLint level, GLint internal_format, + GLsizei width, GLsizei height, GLint border, GLenum format, + GLenum ty, void* data) { + assert(level == 0); + assert(border == 0); + TexStorage2D(target, 1, internal_format, width, height); + TexSubImage2D(target, 0, 0, 0, width, height, format, ty, data); +} + +void TexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, + GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, + GLenum format, GLenum ty, void* data) { + data = get_pixel_unpack_buffer_data(data); + if (!data) return; + Texture& t = ctx->textures[ctx->get_binding(target)]; + prepare_texture(t); + assert(ctx->unpack_row_length == 0 || ctx->unpack_row_length >= width); + GLsizei row_length = + ctx->unpack_row_length != 0 ? ctx->unpack_row_length : width; + if (format == GL_BGRA) { + assert(ty == GL_UNSIGNED_BYTE); + assert(t.internal_format == GL_RGBA8); + } else { + assert(t.internal_format == internal_format_for_data(format, ty)); + } + int bpp = t.bpp(); + if (!bpp || !t.buf) return; + char* src = (char*)data; + assert(xoffset + width <= t.width); + assert(yoffset + height <= t.height); + assert(zoffset + depth <= t.depth); + size_t dest_stride = t.stride(bpp); + for (int z = 0; z < depth; z++) { + char* dest = t.buf + ((zoffset + z) * t.height + yoffset) * dest_stride + + xoffset * bpp; + for (int y = 0; y < height; y++) { + if (t.internal_format == GL_RGBA8 && format != GL_BGRA) { + copy_bgra8_to_rgba8((uint32_t*)dest, (uint32_t*)src, width); + } else { + memcpy(dest, src, width * bpp); + } + dest += dest_stride; + src += row_length * bpp; + } + } +} + +void TexImage3D(GLenum target, GLint level, GLint internal_format, + GLsizei width, GLsizei height, GLsizei depth, GLint border, + GLenum format, GLenum ty, void* data) { + assert(level == 0); + assert(border == 0); + TexStorage3D(target, 1, internal_format, width, height, depth); + TexSubImage3D(target, 0, 0, 0, 0, width, height, depth, format, ty, data); +} + +void TexParameteri(GLenum target, GLenum pname, GLint param) { + Texture& t = ctx->textures[ctx->get_binding(target)]; + switch (pname) { + case GL_TEXTURE_WRAP_S: + assert(param == GL_CLAMP_TO_EDGE); + break; + case GL_TEXTURE_WRAP_T: + assert(param == GL_CLAMP_TO_EDGE); + break; + case GL_TEXTURE_MIN_FILTER: + assert(param == GL_NEAREST || param == GL_LINEAR); + t.min_filter = param; + break; + case GL_TEXTURE_MAG_FILTER: + assert(param == GL_NEAREST || param == GL_LINEAR); + t.mag_filter = param; + break; + default: + break; + } +} + +void GenTextures(int n, GLuint* result) { + for (int i = 0; i < n; i++) { + Texture t; + result[i] = ctx->textures.insert(t); + } +} + +void DeleteTexture(GLuint n) { + if (n && ctx->textures.erase(n)) { + for (size_t i = 0; i < MAX_TEXTURE_UNITS; i++) { + ctx->texture_units[i].unlink(n); + } + } +} + +void GenRenderbuffers(int n, GLuint* result) { + for (int i = 0; i < n; i++) { + Renderbuffer r; + result[i] = ctx->renderbuffers.insert(r); + } +} + +Renderbuffer::~Renderbuffer() { + for (auto* fb : ctx->framebuffers) { + if (fb) { + if (unlink(fb->color_attachment, texture)) { + fb->layer = 0; + } + unlink(fb->depth_attachment, texture); + } + } + DeleteTexture(texture); +} + +void DeleteRenderbuffer(GLuint n) { + if (n && ctx->renderbuffers.erase(n)) { + unlink(ctx->renderbuffer_binding, n); + } +} + +void GenFramebuffers(int n, GLuint* result) { + for (int i = 0; i < n; i++) { + Framebuffer f; + result[i] = ctx->framebuffers.insert(f); + } +} + +void DeleteFramebuffer(GLuint n) { + if (n && ctx->framebuffers.erase(n)) { + unlink(ctx->read_framebuffer_binding, n); + unlink(ctx->draw_framebuffer_binding, n); + } +} + +void RenderbufferStorage(GLenum target, GLenum internal_format, GLsizei width, + GLsizei height) { + // Just refer a renderbuffer to a texture to simplify things for now... + Renderbuffer& r = ctx->renderbuffers[ctx->get_binding(target)]; + if (!r.texture) { + GenTextures(1, &r.texture); + } + switch (internal_format) { + case GL_DEPTH_COMPONENT: + case GL_DEPTH_COMPONENT24: + case GL_DEPTH_COMPONENT32: + // Force depth format to 16 bits... + internal_format = GL_DEPTH_COMPONENT16; + break; + } + set_tex_storage(ctx->textures[r.texture], 1, internal_format, width, height); +} + +void VertexAttribPointer(GLuint index, GLint size, GLenum type, bool normalized, + GLsizei stride, GLuint offset) { + // debugf("cva: %d\n", ctx->current_vertex_array); + VertexArray& v = ctx->vertex_arrays[ctx->current_vertex_array]; + if (index >= NULL_ATTRIB) { + assert(0); + return; + } + VertexAttrib& va = v.attribs[index]; + va.size = size * bytes_per_type(type); + va.type = type; + va.normalized = normalized; + va.stride = stride; + va.offset = offset; + // Buffer &vertex_buf = ctx->buffers[ctx->array_buffer_binding]; + va.vertex_buffer = ctx->array_buffer_binding; + va.vertex_array = ctx->current_vertex_array; + ctx->validate_vertex_array = true; +} + +void VertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride, + GLuint offset) { + // debugf("cva: %d\n", ctx->current_vertex_array); + VertexArray& v = ctx->vertex_arrays[ctx->current_vertex_array]; + if (index >= NULL_ATTRIB) { + assert(0); + return; + } + VertexAttrib& va = v.attribs[index]; + va.size = size * bytes_per_type(type); + va.type = type; + va.normalized = false; + va.stride = stride; + va.offset = offset; + // Buffer &vertex_buf = ctx->buffers[ctx->array_buffer_binding]; + va.vertex_buffer = ctx->array_buffer_binding; + va.vertex_array = ctx->current_vertex_array; + ctx->validate_vertex_array = true; +} + +void EnableVertexAttribArray(GLuint index) { + VertexArray& v = ctx->vertex_arrays[ctx->current_vertex_array]; + if (index >= NULL_ATTRIB) { + assert(0); + return; + } + VertexAttrib& va = v.attribs[index]; + if (!va.enabled) { + ctx->validate_vertex_array = true; + } + va.enabled = true; + v.max_attrib = max(v.max_attrib, (int)index); +} + +void DisableVertexAttribArray(GLuint index) { + VertexArray& v = ctx->vertex_arrays[ctx->current_vertex_array]; + if (index >= NULL_ATTRIB) { + assert(0); + return; + } + VertexAttrib& va = v.attribs[index]; + if (va.enabled) { + ctx->validate_vertex_array = true; + } + va.enabled = false; +} + +void VertexAttribDivisor(GLuint index, GLuint divisor) { + VertexArray& v = ctx->vertex_arrays[ctx->current_vertex_array]; + if (index >= NULL_ATTRIB) { + assert(0); + return; + } + VertexAttrib& va = v.attribs[index]; + va.divisor = divisor; +} + +void BufferData(GLenum target, GLsizeiptr size, void* data, GLenum usage) { + Buffer& b = ctx->buffers[ctx->get_binding(target)]; + if (b.allocate(size)) { + ctx->validate_vertex_array = true; + } + if (data && b.buf && size <= b.size) { + memcpy(b.buf, data, size); + } +} + +void BufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, + void* data) { + Buffer& b = ctx->buffers[ctx->get_binding(target)]; + assert(offset + size <= b.size); + if (data && b.buf && offset + size <= b.size) { + memcpy(&b.buf[offset], data, size); + } +} + +void* MapBuffer(GLenum target, GLbitfield access) { + Buffer& b = ctx->buffers[ctx->get_binding(target)]; + return b.buf; +} + +void* MapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, + GLbitfield access) { + Buffer& b = ctx->buffers[ctx->get_binding(target)]; + if (b.buf && offset >= 0 && length > 0 && offset + length <= b.size) { + return b.buf + offset; + } + return nullptr; +} + +GLboolean UnmapBuffer(GLenum target) { + Buffer& b = ctx->buffers[ctx->get_binding(target)]; + return b.buf != nullptr; +} + +void Uniform1i(GLint location, GLint V0) { + // debugf("tex: %d\n", (int)ctx->textures.size); + if (!program_impl->set_sampler(location, V0)) { + vertex_shader->set_uniform_1i(location, V0); + fragment_shader->set_uniform_1i(location, V0); + } +} +void Uniform4fv(GLint location, GLsizei count, const GLfloat* v) { + vertex_shader->set_uniform_4fv(location, v); + fragment_shader->set_uniform_4fv(location, v); +} +void UniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, + const GLfloat* value) { + vertex_shader->set_uniform_matrix4fv(location, value); + fragment_shader->set_uniform_matrix4fv(location, value); +} + +void FramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, + GLuint texture, GLint level) { + assert(target == GL_READ_FRAMEBUFFER || target == GL_DRAW_FRAMEBUFFER); + Framebuffer& fb = ctx->framebuffers[ctx->get_binding(target)]; + if (attachment == GL_COLOR_ATTACHMENT0) { + fb.color_attachment = texture; + fb.layer = 0; + } else if (attachment == GL_DEPTH_ATTACHMENT) { + fb.depth_attachment = texture; + } else { + assert(0); + } +} + +void FramebufferTextureLayer(GLenum target, GLenum attachment, GLuint texture, + GLint level, GLint layer) { + assert(level == 0); + assert(target == GL_READ_FRAMEBUFFER || target == GL_DRAW_FRAMEBUFFER); + Framebuffer& fb = ctx->framebuffers[ctx->get_binding(target)]; + if (attachment == GL_COLOR_ATTACHMENT0) { + fb.color_attachment = texture; + fb.layer = layer; + } else if (attachment == GL_DEPTH_ATTACHMENT) { + assert(layer == 0); + fb.depth_attachment = texture; + } else { + assert(0); + } +} + +void FramebufferRenderbuffer(GLenum target, GLenum attachment, + GLenum renderbuffertarget, GLuint renderbuffer) { + assert(target == GL_READ_FRAMEBUFFER || target == GL_DRAW_FRAMEBUFFER); + assert(renderbuffertarget == GL_RENDERBUFFER); + Framebuffer& fb = ctx->framebuffers[ctx->get_binding(target)]; + Renderbuffer& rb = ctx->renderbuffers[renderbuffer]; + if (attachment == GL_COLOR_ATTACHMENT0) { + fb.color_attachment = rb.texture; + fb.layer = 0; + } else if (attachment == GL_DEPTH_ATTACHMENT) { + fb.depth_attachment = rb.texture; + } else { + assert(0); + } +} + +} // extern "C" + +static inline Framebuffer* get_framebuffer(GLenum target) { + if (target == GL_FRAMEBUFFER) { + target = GL_DRAW_FRAMEBUFFER; + } + return ctx->framebuffers.find(ctx->get_binding(target)); +} + +template +static inline void fill_n(T* dst, size_t n, T val) { + for (T* end = &dst[n]; dst < end; dst++) *dst = val; +} + +#if USE_SSE2 +template <> +inline void fill_n(uint32_t* dst, size_t n, uint32_t val) { + __asm__ __volatile__("rep stosl\n" + : "+D"(dst), "+c"(n) + : "a"(val) + : "memory", "cc"); +} +#endif + +template +static void clear_buffer(Texture& t, T value, int x0, int x1, int y0, int y1, + int layer = 0, int skip_start = 0, int skip_end = 0) { + if (!t.buf) return; + skip_start = max(skip_start, x0); + skip_end = max(skip_end, skip_start); + size_t stride = t.stride(sizeof(T)); + if (x1 - x0 == t.width && y1 - y0 > 1 && skip_start >= skip_end) { + x1 += (stride / sizeof(T)) * (y1 - y0 - 1); + y1 = y0 + 1; + } + char* buf = t.buf + stride * (t.height * layer + y0) + x0 * sizeof(T); + uint32_t chunk = + sizeof(T) == 1 + ? uint32_t(value) * 0x01010101U + : (sizeof(T) == 2 ? uint32_t(value) | (uint32_t(value) << 16) + : value); + for (int y = y0; y < y1; y++) { + if (x0 < skip_start) { + fill_n((uint32_t*)buf, (skip_start - x0) / (4 / sizeof(T)), chunk); + if (sizeof(T) < 4) { + fill_n((T*)buf + ((skip_start - x0) & ~(4 / sizeof(T) - 1)), + (skip_start - x0) & (4 / sizeof(T) - 1), value); + } + } + if (skip_end < x1) { + T* skip_buf = (T*)buf + (skip_end - x0); + fill_n((uint32_t*)skip_buf, (x1 - skip_end) / (4 / sizeof(T)), chunk); + if (sizeof(T) < 4) { + fill_n(skip_buf + ((x1 - skip_end) & ~(4 / sizeof(T) - 1)), + (x1 - skip_end) & (4 / sizeof(T) - 1), value); + } + } + buf += stride; + } +} + +template +static inline void clear_buffer(Texture& t, T value, int layer = 0) { + int x0 = 0, y0 = 0, x1 = t.width, y1 = t.height; + if (ctx->scissortest) { + x0 = max(x0, ctx->scissor.x); + y0 = max(y0, ctx->scissor.y); + x1 = min(x1, ctx->scissor.x + ctx->scissor.width); + y1 = min(y1, ctx->scissor.y + ctx->scissor.height); + } + if (x1 - x0 > 0) { + clear_buffer(t, value, x0, x1, y0, y1, layer); + } +} + +template +static void force_clear(Texture& t, const IntRect* skip = nullptr) { + if (!t.delay_clear || !t.cleared_rows) { + return; + } + int y0 = 0; + int y1 = t.height; + int skip_start = 0; + int skip_end = 0; + if (skip) { + y0 = min(max(skip->y, 0), t.height); + y1 = min(max(skip->y + skip->height, y0), t.height); + skip_start = min(max(skip->x, 0), t.width); + skip_end = min(max(skip->x + skip->width, y0), t.width); + if (skip_start <= 0 && skip_end >= t.width && y0 <= 0 && y1 >= t.height) { + t.disable_delayed_clear(); + return; + } + } + int num_masks = (y1 + 31) / 32; + uint32_t* rows = t.cleared_rows; + for (int i = y0 / 32; i < num_masks; i++) { + uint32_t mask = rows[i]; + if (mask != ~0U) { + rows[i] = ~0U; + int start = i * 32; + while (mask) { + int count = __builtin_ctz(mask); + if (count > 0) { + clear_buffer(t, t.clear_val, 0, t.width, start, start + count, 0, + skip_start, skip_end); + t.delay_clear -= count; + start += count; + mask >>= count; + } + count = __builtin_ctz(mask + 1); + start += count; + mask >>= count; + } + int count = (i + 1) * 32 - start; + if (count > 0) { + clear_buffer(t, t.clear_val, 0, t.width, start, start + count, 0, + skip_start, skip_end); + t.delay_clear -= count; + } + } + } + if (t.delay_clear <= 0) t.disable_delayed_clear(); +} + +static void prepare_texture(Texture& t, const IntRect* skip) { + if (t.delay_clear) { + switch (t.internal_format) { + case GL_RGBA8: + force_clear(t, skip); + break; + case GL_R8: + force_clear(t, skip); + break; + case GL_DEPTH_COMPONENT16: + force_clear(t, skip); + break; + default: + assert(false); + break; + } + } +} + +extern "C" { + +void InitDefaultFramebuffer(int width, int height) { + Framebuffer& fb = ctx->framebuffers[0]; + if (!fb.color_attachment) { + GenTextures(1, &fb.color_attachment); + fb.layer = 0; + } + Texture& colortex = ctx->textures[fb.color_attachment]; + if (colortex.width != width || colortex.height != height) { + colortex.cleanup(); + set_tex_storage(colortex, 1, GL_RGBA8, width, height); + } + if (!fb.depth_attachment) { + GenTextures(1, &fb.depth_attachment); + } + Texture& depthtex = ctx->textures[fb.depth_attachment]; + if (depthtex.width != width || depthtex.height != height) { + depthtex.cleanup(); + set_tex_storage(depthtex, 1, GL_DEPTH_COMPONENT16, width, height); + } +} + +void* GetColorBuffer(GLuint fbo, GLboolean flush, int32_t* width, + int32_t* height) { + Framebuffer* fb = ctx->framebuffers.find(fbo); + if (!fb || !fb->color_attachment) { + return nullptr; + } + Texture& colortex = ctx->textures[fb->color_attachment]; + if (flush) { + prepare_texture(colortex); + } + *width = colortex.width; + *height = colortex.height; + return colortex.buf + ? colortex.buf + + (fb->layer ? fb->layer * colortex.layer_stride() : 0) + : nullptr; +} + +void SetTextureBuffer(GLuint texid, GLenum internal_format, GLsizei width, + GLsizei height, void* buf, GLsizei min_width, + GLsizei min_height) { + Texture& t = ctx->textures[texid]; + set_tex_storage(t, 1, internal_format, width, height, !buf, buf, min_width, + min_height); +} + +GLenum CheckFramebufferStatus(GLenum target) { + Framebuffer* fb = get_framebuffer(target); + if (!fb || !fb->color_attachment) { + return GL_FRAMEBUFFER_UNSUPPORTED; + } + return GL_FRAMEBUFFER_COMPLETE; +} + +static inline bool clear_requires_scissor(Texture& t) { + return ctx->scissortest && + (ctx->scissor.x > 0 || ctx->scissor.y > 0 || + ctx->scissor.width < t.width || ctx->scissor.height < t.height); +} + +void Clear(GLbitfield mask) { + Framebuffer& fb = *get_framebuffer(GL_DRAW_FRAMEBUFFER); + if ((mask & GL_COLOR_BUFFER_BIT) && fb.color_attachment) { + Texture& t = ctx->textures[fb.color_attachment]; + if (t.internal_format == GL_RGBA8) { + uint32_t color = ctx->clearcolor; + if (clear_requires_scissor(t)) { + force_clear(t, &ctx->scissor); + clear_buffer(t, color, fb.layer); + } else if (t.depth > 1) { + t.disable_delayed_clear(); + clear_buffer(t, color, fb.layer); + } else { + t.enable_delayed_clear(color); + } + } else if (t.internal_format == GL_R8) { + uint8_t color = uint8_t((ctx->clearcolor >> 16) & 0xFF); + if (clear_requires_scissor(t)) { + force_clear(t, &ctx->scissor); + clear_buffer(t, color, fb.layer); + } else if (t.depth > 1) { + t.disable_delayed_clear(); + clear_buffer(t, color, fb.layer); + } else { + t.enable_delayed_clear(color); + } + } else { + assert(false); + } + } + if ((mask & GL_DEPTH_BUFFER_BIT) && fb.depth_attachment) { + Texture& t = ctx->textures[fb.depth_attachment]; + assert(t.internal_format == GL_DEPTH_COMPONENT16); + uint16_t depth = uint16_t(0xFFFF * ctx->cleardepth) - 0x8000; + if (clear_requires_scissor(t)) { + force_clear(t, &ctx->scissor); + clear_buffer(t, depth); + } else { + t.enable_delayed_clear(depth); + } + } +} + +void InvalidateFramebuffer(GLenum target, GLsizei num_attachments, + const GLenum* attachments) { + Framebuffer* fb = get_framebuffer(target); + if (!fb || num_attachments <= 0 || !attachments) { + return; + } + for (GLsizei i = 0; i < num_attachments; i++) { + switch (attachments[i]) { + case GL_DEPTH_ATTACHMENT: { + Texture& t = ctx->textures[fb->depth_attachment]; + t.disable_delayed_clear(); + break; + } + case GL_COLOR_ATTACHMENT0: { + Texture& t = ctx->textures[fb->color_attachment]; + t.disable_delayed_clear(); + break; + } + } + } +} + +void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, + GLenum type, void* data) { + data = get_pixel_pack_buffer_data(data); + if (!data) return; + Framebuffer* fb = get_framebuffer(GL_READ_FRAMEBUFFER); + if (!fb) return; + assert(format == GL_RED || format == GL_RGBA || format == GL_RGBA_INTEGER || + format == GL_BGRA); + Texture& t = ctx->textures[fb->color_attachment]; + if (!t.buf) return; + prepare_texture(t); + // debugf("read pixels %d, %d, %d, %d from fb %d with format %x\n", x, y, + // width, height, ctx->read_framebuffer_binding, t.internal_format); + assert(x + width <= t.width); + assert(y + height <= t.height); + if (internal_format_for_data(format, type) != t.internal_format) { + debugf("mismatched format for read pixels: %x vs %x\n", t.internal_format, + internal_format_for_data(format, type)); + assert(false); + } + int bpp = t.bpp(); + char* dest = (char*)data; + size_t src_stride = t.stride(bpp); + char* src = t.buf + (t.height * fb->layer + y) * src_stride + x * bpp; + for (; height > 0; height--) { + if (t.internal_format == GL_RGBA8 && format != GL_BGRA) { + copy_bgra8_to_rgba8((uint32_t*)dest, (uint32_t*)src, width); + } else { + memcpy(dest, src, width * bpp); + } + dest += width * bpp; + src += src_stride; + } +} + +void CopyImageSubData(GLuint srcName, GLenum srcTarget, GLint srcLevel, + GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, + GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, + GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, + GLsizei srcDepth) { + if (srcTarget == GL_RENDERBUFFER) { + Renderbuffer& rb = ctx->renderbuffers[srcName]; + srcName = rb.texture; + } + if (dstTarget == GL_RENDERBUFFER) { + Renderbuffer& rb = ctx->renderbuffers[dstName]; + dstName = rb.texture; + } + Texture& srctex = ctx->textures[srcName]; + if (!srctex.buf) return; + prepare_texture(srctex); + Texture& dsttex = ctx->textures[dstName]; + if (!dsttex.buf) return; + IntRect skip = {dstX, dstY, srcWidth, abs(srcHeight)}; + prepare_texture(dsttex, &skip); + assert(srctex.internal_format == dsttex.internal_format); + assert(srcX + srcWidth <= srctex.width); + assert(srcY + srcHeight <= srctex.height); + assert(srcZ + srcDepth <= max(srctex.depth, 1)); + assert(dstX + srcWidth <= dsttex.width); + assert(max(dstY, dstY + srcHeight) <= dsttex.height); + assert(dstZ + srcDepth <= max(dsttex.depth, 1)); + int bpp = srctex.bpp(); + int src_stride = srctex.stride(bpp); + int dest_stride = dsttex.stride(bpp); + for (int z = 0; z < srcDepth; z++) { + char* dest = dsttex.buf + + (dsttex.height * (dstZ + z) + dstY) * dest_stride + dstX * bpp; + char* src = srctex.buf + (srctex.height * (srcZ + z) + srcY) * src_stride + + srcX * bpp; + if (srcHeight < 0) { + for (int y = srcHeight; y < 0; y++) { + dest -= dest_stride; + memcpy(dest, src, srcWidth * bpp); + src += src_stride; + } + } else { + for (int y = 0; y < srcHeight; y++) { + memcpy(dest, src, srcWidth * bpp); + dest += dest_stride; + src += src_stride; + } + } + } +} + +void CopyTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, + GLint zoffset, GLint x, GLint y, GLsizei width, + GLsizei height) { + Framebuffer* fb = get_framebuffer(GL_READ_FRAMEBUFFER); + if (!fb) return; + CopyImageSubData(fb->color_attachment, GL_TEXTURE_3D, 0, x, y, fb->layer, + ctx->get_binding(target), GL_TEXTURE_3D, 0, xoffset, yoffset, + zoffset, width, height, 1); +} + +void CopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, + GLint x, GLint y, GLsizei width, GLsizei height) { + Framebuffer* fb = get_framebuffer(GL_READ_FRAMEBUFFER); + if (!fb) return; + CopyImageSubData(fb->color_attachment, GL_TEXTURE_2D_ARRAY, 0, x, y, + fb->layer, ctx->get_binding(target), GL_TEXTURE_2D_ARRAY, 0, + xoffset, yoffset, 0, width, height, 1); +} + +void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, + GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, + GLbitfield mask, GLenum filter) { + assert(mask == GL_COLOR_BUFFER_BIT); + Framebuffer* srcfb = get_framebuffer(GL_READ_FRAMEBUFFER); + if (!srcfb) return; + Framebuffer* dstfb = get_framebuffer(GL_DRAW_FRAMEBUFFER); + if (!dstfb) return; + int dstWidth = dstX1 - dstX0; + int dstHeight = dstY1 - dstY0; + assert(srcX1 - srcX0 == dstWidth && srcY1 - srcY0 == abs(dstHeight)); + CopyImageSubData(srcfb->color_attachment, GL_TEXTURE_2D_ARRAY, 0, srcX0, + srcY0, srcfb->layer, dstfb->color_attachment, + GL_TEXTURE_2D_ARRAY, 0, dstX0, dstY0, dstfb->layer, dstWidth, + dstHeight, 1); +} + +} // extern "C" + +using PackedRGBA8 = V16; +using WideRGBA8 = V16; +using HalfRGBA8 = V8; + +static inline WideRGBA8 unpack(PackedRGBA8 p) { return CONVERT(p, WideRGBA8); } + +static inline PackedRGBA8 pack(WideRGBA8 p) { +#if USE_SSE2 + return _mm_packus_epi16(lowHalf(p), highHalf(p)); +#elif USE_NEON + return vcombine_u8(vqmovn_u16(lowHalf(p)), vqmovn_u16(highHalf(p))); +#else + return CONVERT(p, PackedRGBA8); +#endif +} + +static inline HalfRGBA8 packRGBA8(I32 a, I32 b) { +#if USE_SSE2 + return _mm_packs_epi32(a, b); +#elif USE_NEON + return vcombine_u16(vqmovun_s32(a), vqmovun_s32(b)); +#else + return CONVERT(combine(a, b), HalfRGBA8); +#endif +} + +using PackedR8 = V4; +using WideR8 = V4; + +static inline WideR8 unpack(PackedR8 p) { return CONVERT(p, WideR8); } + +static inline WideR8 packR8(I32 a) { +#if USE_SSE2 + return lowHalf(bit_cast>(_mm_packs_epi32(a, a))); +#elif USE_NEON + return vqmovun_s32(a); +#else + return CONVERT(a, WideR8); +#endif +} + +static inline PackedR8 pack(WideR8 p) { +#if USE_SSE2 + auto m = expand(p); + auto r = bit_cast>(_mm_packus_epi16(m, m)); + return SHUFFLE(r, r, 0, 1, 2, 3); +#elif USE_NEON + return lowHalf(bit_cast>(vqmovn_u16(expand(p)))); +#else + return CONVERT(p, PackedR8); +#endif +} + +using ZMask4 = V4; +using ZMask8 = V8; + +static inline PackedRGBA8 unpack(ZMask4 mask, uint32_t*) { + return bit_cast(mask.xxyyzzww); +} + +static inline WideR8 unpack(ZMask4 mask, uint8_t*) { + return bit_cast(mask); +} + +#if USE_SSE2 +# define ZMASK_NONE_PASSED 0xFFFF +# define ZMASK_ALL_PASSED 0 +static inline uint32_t zmask_code(ZMask8 mask) { + return _mm_movemask_epi8(mask); +} +static inline uint32_t zmask_code(ZMask4 mask) { + return zmask_code(mask.xyzwxyzw); +} +#else +using ZMask4Code = V4; +using ZMask8Code = V8; +# define ZMASK_NONE_PASSED 0xFFFFFFFFU +# define ZMASK_ALL_PASSED 0 +static inline uint32_t zmask_code(ZMask4 mask) { + return bit_cast(CONVERT(mask, ZMask4Code)); +} +static inline uint32_t zmask_code(ZMask8 mask) { + return zmask_code( + ZMask4((U16(lowHalf(mask)) >> 12) | (U16(highHalf(mask)) << 4))); +} +#endif + +template +static ALWAYS_INLINE int check_depth8(uint16_t z, uint16_t* zbuf, + ZMask8& outmask) { + ZMask8 dest = unaligned_load(zbuf); + ZMask8 src = int16_t(z); + // Invert the depth test to check which pixels failed and should be discarded. + ZMask8 mask = FUNC == GL_LEQUAL ? + // GL_LEQUAL: Not(LessEqual) = Greater + ZMask8(src > dest) + : + // GL_LESS: Not(Less) = GreaterEqual + ZMask8(src >= dest); + switch (zmask_code(mask)) { + case ZMASK_NONE_PASSED: + return 0; + case ZMASK_ALL_PASSED: + if (MASK) { + unaligned_store(zbuf, src); + } + return -1; + default: + if (MASK) { + unaligned_store(zbuf, (mask & dest) | (~mask & src)); + } + outmask = mask; + return 1; + } +} + +template +static ALWAYS_INLINE bool check_depth4(uint16_t z, uint16_t* zbuf, + ZMask4& outmask, int span = 0) { + ZMask4 dest = unaligned_load(zbuf); + ZMask4 src = int16_t(z); + // Invert the depth test to check which pixels failed and should be discarded. + ZMask4 mask = ctx->depthfunc == GL_LEQUAL + ? + // GL_LEQUAL: Not(LessEqual) = Greater + ZMask4(src > dest) + : + // GL_LESS: Not(Less) = GreaterEqual + ZMask4(src >= dest); + if (!FULL_SPANS) { + mask |= ZMask4(span) < ZMask4{1, 2, 3, 4}; + } + if (zmask_code(mask) == ZMASK_NONE_PASSED) { + return false; + } + if (!DISCARD && ctx->depthmask) { + unaligned_store(zbuf, (mask & dest) | (~mask & src)); + } + outmask = mask; + return true; +} + +static inline ZMask4 packZMask4(Bool a) { +#if USE_SSE2 + return lowHalf(bit_cast(_mm_packs_epi32(a, a))); +#elif USE_NEON + return vqmovun_s32(a); +#else + return CONVERT(a, ZMask4); +#endif +} + +static ALWAYS_INLINE void discard_depth(uint16_t z, uint16_t* zbuf, + ZMask4 mask) { + if (ctx->depthmask) { + ZMask4 dest = unaligned_load(zbuf); + ZMask4 src = int16_t(z); + mask |= packZMask4(fragment_shader->isPixelDiscarded); + unaligned_store(zbuf, (mask & dest) | (~mask & src)); + } +} + +static inline WideRGBA8 pack_pixels_RGBA8(const vec4& v) { + ivec4 i = roundto(v, 255.49f); + HalfRGBA8 xz = packRGBA8(i.z, i.x); + HalfRGBA8 yw = packRGBA8(i.y, i.w); + HalfRGBA8 xy = zipLow(xz, yw); + HalfRGBA8 zw = zipHigh(xz, yw); + HalfRGBA8 lo = zip2Low(xy, zw); + HalfRGBA8 hi = zip2High(xy, zw); + return combine(lo, hi); +} + +static inline WideRGBA8 pack_pixels_RGBA8(const vec4_scalar& v) { + I32 i = roundto((Float){v.z, v.y, v.x, v.w}, 255.49f); + HalfRGBA8 c = packRGBA8(i, i); + return combine(c, c); +} + +static inline WideRGBA8 pack_pixels_RGBA8() { + return pack_pixels_RGBA8(fragment_shader->gl_FragColor); +} + +template +static inline PackedRGBA8 pack_span(uint32_t*, const V& v) { + return pack(pack_pixels_RGBA8(v)); +} + +static inline PackedRGBA8 pack_span(uint32_t*) { + return pack(pack_pixels_RGBA8()); +} + +// (x*y + x) >> 8, cheap approximation of (x*y) / 255 +template +static inline T muldiv255(T x, T y) { + return (x * y + x) >> 8; +} +static inline WideRGBA8 alphas(WideRGBA8 c) { + return SHUFFLE(c, c, 3, 3, 3, 3, 7, 7, 7, 7, 11, 11, 11, 11, 15, 15, 15, 15); +} + +static inline WideRGBA8 blend_pixels_RGBA8(PackedRGBA8 pdst, WideRGBA8 src) { + WideRGBA8 dst = unpack(pdst); + const WideRGBA8 RGB_MASK = {0xFFFF, 0xFFFF, 0xFFFF, 0, 0xFFFF, 0xFFFF, + 0xFFFF, 0, 0xFFFF, 0xFFFF, 0xFFFF, 0, + 0xFFFF, 0xFFFF, 0xFFFF, 0}; + const WideRGBA8 ALPHA_MASK = {0, 0, 0, 0xFFFF, 0, 0, 0, 0xFFFF, + 0, 0, 0, 0xFFFF, 0, 0, 0, 0xFFFF}; + switch (blend_key) { + case BLEND_KEY_NONE: + return src; + case BLEND_KEY(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE): + return dst + muldiv255((src - dst) | ALPHA_MASK, alphas(src)); + case BLEND_KEY(GL_ONE, GL_ONE_MINUS_SRC_ALPHA): + return src + dst - muldiv255(dst, alphas(src)); + case BLEND_KEY(GL_ZERO, GL_ONE_MINUS_SRC_COLOR): + return dst - muldiv255(dst, src); + case BLEND_KEY(GL_ZERO, GL_ONE_MINUS_SRC_COLOR, GL_ZERO, GL_ONE): + return dst - (muldiv255(dst, src) & RGB_MASK); + case BLEND_KEY(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA): + return dst - muldiv255(dst, alphas(src)); + case BLEND_KEY(GL_ZERO, GL_SRC_COLOR): + return muldiv255(src, dst); + case BLEND_KEY(GL_ONE, GL_ONE): + return src + dst; + case BLEND_KEY(GL_ONE, GL_ONE, GL_ONE, GL_ONE_MINUS_SRC_ALPHA): + return src + dst - (muldiv255(dst, src) & ALPHA_MASK); + case BLEND_KEY(GL_ONE, GL_ZERO): + return src; + case BLEND_KEY(GL_ONE_MINUS_DST_ALPHA, GL_ONE, GL_ZERO, GL_ONE): + return dst + ((src - muldiv255(src, alphas(src))) & RGB_MASK); + case BLEND_KEY(GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_COLOR): + return dst + + muldiv255(combine(ctx->blendcolor, ctx->blendcolor) - dst, src); + case BLEND_KEY(GL_ONE, GL_ONE_MINUS_SRC1_COLOR): { + WideRGBA8 secondary = + pack_pixels_RGBA8(fragment_shader->gl_SecondaryFragColor); + return src + dst - muldiv255(dst, secondary); + } + default: + UNREACHABLE; + // return src; + } +} + +template +static inline void commit_output(uint32_t* buf, PackedRGBA8 mask) { + fragment_shader->run(); + PackedRGBA8 dst = unaligned_load(buf); + WideRGBA8 r = pack_pixels_RGBA8(); + if (blend_key) r = blend_pixels_RGBA8(dst, r); + if (DISCARD) mask |= bit_cast(fragment_shader->isPixelDiscarded); + unaligned_store(buf, (mask & dst) | (~mask & pack(r))); +} + +template +static inline void commit_output(uint32_t* buf) { + commit_output(buf, 0); +} + +template <> +inline void commit_output(uint32_t* buf) { + fragment_shader->run(); + WideRGBA8 r = pack_pixels_RGBA8(); + if (blend_key) r = blend_pixels_RGBA8(unaligned_load(buf), r); + unaligned_store(buf, pack(r)); +} + +static inline void commit_span(uint32_t* buf, PackedRGBA8 r) { + if (blend_key) + r = pack(blend_pixels_RGBA8(unaligned_load(buf), unpack(r))); + unaligned_store(buf, r); +} + +UNUSED static inline void commit_solid_span(uint32_t* buf, PackedRGBA8 r, + int len) { + if (blend_key) { + auto src = unpack(r); + for (uint32_t* end = &buf[len]; buf < end; buf += 4) { + unaligned_store( + buf, pack(blend_pixels_RGBA8(unaligned_load(buf), src))); + } + } else { + fill_n(buf, len, bit_cast(r).x); + } +} + +UNUSED static inline void commit_texture_span(uint32_t* buf, uint32_t* src, + int len) { + if (blend_key) { + for (uint32_t* end = &buf[len]; buf < end; buf += 4, src += 4) { + PackedRGBA8 r = unaligned_load(src); + unaligned_store(buf, pack(blend_pixels_RGBA8( + unaligned_load(buf), unpack(r)))); + } + } else { + memcpy(buf, src, len * sizeof(uint32_t)); + } +} + +template +static inline void commit_output(uint32_t* buf, uint16_t z, uint16_t* zbuf) { + ZMask4 zmask; + if (check_depth4(z, zbuf, zmask)) { + commit_output(buf, unpack(zmask, buf)); + if (DISCARD) { + discard_depth(z, zbuf, zmask); + } + } else { + fragment_shader->skip(); + } +} + +template +static inline void commit_output(uint32_t* buf, uint16_t z, uint16_t* zbuf, + int span) { + ZMask4 zmask; + if (check_depth4(z, zbuf, zmask, span)) { + commit_output(buf, unpack(zmask, buf)); + if (DISCARD) { + discard_depth(z, zbuf, zmask); + } + } +} + +static inline PackedRGBA8 span_mask_RGBA8(int span) { + return bit_cast(I32(span) < I32{1, 2, 3, 4}); +} + +template +static inline void commit_output(uint32_t* buf, int span) { + commit_output(buf, span_mask_RGBA8(span)); +} + +static inline WideR8 pack_pixels_R8(Float c) { + return packR8(roundto(c, 255.49f)); +} + +static inline WideR8 pack_pixels_R8() { + return pack_pixels_R8(fragment_shader->gl_FragColor.x); +} + +template +static inline PackedR8 pack_span(uint8_t*, C c) { + return pack(pack_pixels_R8(c)); +} + +static inline PackedR8 pack_span(uint8_t*) { return pack(pack_pixels_R8()); } + +static inline WideR8 blend_pixels_R8(WideR8 dst, WideR8 src) { + switch (blend_key) { + case BLEND_KEY_NONE: + return src; + case BLEND_KEY(GL_ZERO, GL_SRC_COLOR): + return muldiv255(src, dst); + case BLEND_KEY(GL_ONE, GL_ONE): + return src + dst; + case BLEND_KEY(GL_ONE, GL_ZERO): + return src; + default: + UNREACHABLE; + // return src; + } +} + +template +static inline void commit_output(uint8_t* buf, WideR8 mask) { + fragment_shader->run(); + WideR8 dst = unpack(unaligned_load(buf)); + WideR8 r = pack_pixels_R8(); + if (blend_key) r = blend_pixels_R8(dst, r); + if (DISCARD) mask |= packR8(fragment_shader->isPixelDiscarded); + unaligned_store(buf, pack((mask & dst) | (~mask & r))); +} + +template +static inline void commit_output(uint8_t* buf) { + commit_output(buf, 0); +} + +template <> +inline void commit_output(uint8_t* buf) { + fragment_shader->run(); + WideR8 r = pack_pixels_R8(); + if (blend_key) r = blend_pixels_R8(unpack(unaligned_load(buf)), r); + unaligned_store(buf, pack(r)); +} + +static inline void commit_span(uint8_t* buf, PackedR8 r) { + if (blend_key) + r = pack(blend_pixels_R8(unpack(unaligned_load(buf)), unpack(r))); + unaligned_store(buf, r); +} + +UNUSED static inline void commit_solid_span(uint8_t* buf, PackedR8 r, int len) { + if (blend_key) { + auto src = unpack(r); + for (uint8_t* end = &buf[len]; buf < end; buf += 4) { + unaligned_store(buf, pack(blend_pixels_R8( + unpack(unaligned_load(buf)), src))); + } + } else { + fill_n((uint32_t*)buf, len / 4, bit_cast(r)); + } +} + +template +static inline void commit_output(uint8_t* buf, uint16_t z, uint16_t* zbuf) { + ZMask4 zmask; + if (check_depth4(z, zbuf, zmask)) { + commit_output(buf, unpack(zmask, buf)); + if (DISCARD) { + discard_depth(z, zbuf, zmask); + } + } else { + fragment_shader->skip(); + } +} + +template +static inline void commit_output(uint8_t* buf, uint16_t z, uint16_t* zbuf, + int span) { + ZMask4 zmask; + if (check_depth4(z, zbuf, zmask, span)) { + commit_output(buf, unpack(zmask, buf)); + if (DISCARD) { + discard_depth(z, zbuf, zmask); + } + } +} + +static inline WideR8 span_mask_R8(int span) { + return bit_cast(WideR8(span) < WideR8{1, 2, 3, 4}); +} + +template +static inline void commit_output(uint8_t* buf, int span) { + commit_output(buf, span_mask_R8(span)); +} + +static const size_t MAX_FLATS = 64; +typedef float Flats[MAX_FLATS]; + +static const size_t MAX_INTERPOLANTS = 16; +typedef VectorType Interpolants; + +template +static ALWAYS_INLINE void dispatch_draw_span(S* shader, P* buf, int len) { + int drawn = shader->draw_span(buf, len); + if (drawn) shader->step_interp_inputs(drawn >> 2); + for (buf += drawn; drawn < len; drawn += 4, buf += 4) { + S::run(shader); + commit_span(buf, pack_span(buf)); + } +} + +#include "texture.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wuninitialized" +#pragma GCC diagnostic ignored "-Wunused-function" +#pragma GCC diagnostic ignored "-Wunused-parameter" +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wimplicit-fallthrough" +#include "load_shader.h" +#pragma GCC diagnostic pop + +template +static inline void draw_depth_span(uint16_t z, P* buf, uint16_t* depth, + int span) { + int skip = 0; + if (fragment_shader->has_draw_span(buf)) { + int len = 0; + do { + ZMask8 zmask; + switch (check_depth8(z, depth, zmask)) { + case 0: + if (len) { + fragment_shader->draw_span(buf - len, len); + len = 0; + } + skip += 2; + break; + case -1: + if (skip) { + fragment_shader->skip(skip); + skip = 0; + } + len += 8; + break; + default: + if (len) { + fragment_shader->draw_span(buf - len, len); + len = 0; + } else if (skip) { + fragment_shader->skip(skip); + skip = 0; + } + commit_output(buf, unpack(lowHalf(zmask), buf)); + commit_output(buf + 4, unpack(highHalf(zmask), buf)); + break; + } + buf += 8; + depth += 8; + span -= 8; + } while (span >= 8); + if (len) { + fragment_shader->draw_span(buf - len, len); + } + } else { + do { + ZMask8 zmask; + switch (check_depth8(z, depth, zmask)) { + case 0: + skip += 2; + break; + case -1: + if (skip) { + fragment_shader->skip(skip); + skip = 0; + } + commit_output(buf); + commit_output(buf + 4); + break; + default: + if (skip) { + fragment_shader->skip(skip); + skip = 0; + } + commit_output(buf, unpack(lowHalf(zmask), buf)); + commit_output(buf + 4, unpack(highHalf(zmask), buf)); + break; + } + buf += 8; + depth += 8; + span -= 8; + } while (span >= 8); + } + if (skip) { + fragment_shader->skip(skip); + } +} + +typedef vec2_scalar Point; + +template +static inline void draw_quad_spans(int nump, Point p[4], uint16_t z, + Interpolants interp_outs[4], + Texture& colortex, int layer, + Texture& depthtex, float fx0, float fy0, + float fx1, float fy1) { + Point l0, r0, l1, r1; + int l0i, r0i, l1i, r1i; + { + int top = nump > 3 && p[3].y < p[2].y + ? (p[0].y < p[1].y ? (p[0].y < p[3].y ? 0 : 3) + : (p[1].y < p[3].y ? 1 : 3)) + : (p[0].y < p[1].y ? (p[0].y < p[2].y ? 0 : 2) + : (p[1].y < p[2].y ? 1 : 2)); +#define NEXT_POINT(idx) \ + ({ \ + int cur = (idx) + 1; \ + cur < nump ? cur : 0; \ + }) +#define PREV_POINT(idx) \ + ({ \ + int cur = (idx)-1; \ + cur >= 0 ? cur : nump - 1; \ + }) + int next = NEXT_POINT(top); + int prev = PREV_POINT(top); + if (p[top].y == p[next].y) { + l0i = next; + l1i = NEXT_POINT(next); + r0i = top; + r1i = prev; + } else if (p[top].y == p[prev].y) { + l0i = top; + l1i = next; + r0i = prev; + r1i = PREV_POINT(prev); + } else { + l0i = r0i = top; + l1i = next; + r1i = prev; + } + l0 = p[l0i]; + r0 = p[r0i]; + l1 = p[l1i]; + r1 = p[r1i]; + // debugf("l0: %d(%f,%f), r0: %d(%f,%f) -> l1: %d(%f,%f), r1: + // %d(%f,%f)\n", l0i, l0.x, l0.y, r0i, r0.x, r0.y, l1i, l1.x, l1.y, r1i, + // r1.x, r1.y); + } + + float lx = l0.x; + float lk = 1.0f / (l1.y - l0.y); + float lm = (l1.x - l0.x) * lk; + float rx = r0.x; + float rk = 1.0f / (r1.y - r0.y); + float rm = (r1.x - r0.x) * rk; + assert(l0.y == r0.y); + float y = floor(max(l0.y, fy0) + 0.5f) + 0.5f; + lx += (y - l0.y) * lm; + rx += (y - r0.y) * rm; + Interpolants lo = interp_outs[l0i]; + Interpolants lom = (interp_outs[l1i] - lo) * lk; + lo = lo + lom * (y - l0.y); + Interpolants ro = interp_outs[r0i]; + Interpolants rom = (interp_outs[r1i] - ro) * rk; + ro = ro + rom * (y - r0.y); + P* fbuf = (P*)colortex.buf + (layer * colortex.height + int(y)) * + colortex.stride(sizeof(P)) / sizeof(P); + uint16_t* fdepth = + (uint16_t*)depthtex.buf + + int(y) * depthtex.stride(sizeof(uint16_t)) / sizeof(uint16_t); + while (y < fy1) { + if (y > l1.y) { + l0i = l1i; + l0 = l1; + l1i = NEXT_POINT(l1i); + l1 = p[l1i]; + if (l1.y <= l0.y) break; + lk = 1.0f / (l1.y - l0.y); + lm = (l1.x - l0.x) * lk; + lx = l0.x + (y - l0.y) * lm; + lo = interp_outs[l0i]; + lom = (interp_outs[l1i] - lo) * lk; + lo += lom * (y - l0.y); + } + if (y > r1.y) { + r0i = r1i; + r0 = r1; + r1i = PREV_POINT(r1i); + r1 = p[r1i]; + if (r1.y <= r0.y) break; + rk = 1.0f / (r1.y - r0.y); + rm = (r1.x - r0.x) * rk; + rx = r0.x + (y - r0.y) * rm; + ro = interp_outs[r0i]; + rom = (interp_outs[r1i] - ro) * rk; + ro += rom * (y - r0.y); + } + int startx = int(max(min(lx, rx), fx0) + 0.5f); + int endx = int(min(max(lx, rx), fx1) + 0.5f); + int span = endx - startx; + if (span > 0) { + ctx->shaded_rows++; + ctx->shaded_pixels += span; + P* buf = fbuf + startx; + uint16_t* depth = fdepth + startx; + bool use_depth = depthtex.buf != nullptr; + bool use_discard = fragment_shader->use_discard(); + if (depthtex.delay_clear) { + int yi = int(y); + uint32_t& mask = depthtex.cleared_rows[yi / 32]; + if ((mask & (1 << (yi & 31))) == 0) { + switch (ctx->depthfunc) { + case GL_LESS: + if (int16_t(z) < int16_t(depthtex.clear_val)) + break; + else + goto next_span; + case GL_LEQUAL: + if (int16_t(z) <= int16_t(depthtex.clear_val)) + break; + else + goto next_span; + } + if (ctx->depthmask) { + mask |= 1 << (yi & 31); + depthtex.delay_clear--; + if (use_discard) { + clear_buffer(depthtex, depthtex.clear_val, 0, + depthtex.width, yi, yi + 1); + } else { + if (startx > 0 || endx < depthtex.width) { + clear_buffer(depthtex, depthtex.clear_val, 0, + depthtex.width, yi, yi + 1, 0, startx, + endx); + } + clear_buffer(depthtex, z, startx, endx, yi, yi + 1); + use_depth = false; + } + } else { + use_depth = false; + } + } + } + if (colortex.delay_clear) { + int yi = int(y); + uint32_t& mask = colortex.cleared_rows[yi / 32]; + if ((mask & (1 << (yi & 31))) == 0) { + mask |= 1 << (yi & 31); + colortex.delay_clear--; + if (use_depth || blend_key || use_discard) { + clear_buffer

(colortex, colortex.clear_val, 0, colortex.width, yi, + yi + 1, layer); + } else if (startx > 0 || endx < colortex.width) { + clear_buffer

(colortex, colortex.clear_val, 0, colortex.width, yi, + yi + 1, layer, startx, endx); + } + } + } + fragment_shader->gl_FragCoordXY.x = init_interp(startx + 0.5f, 1); + fragment_shader->gl_FragCoordXY.y = y; + { + Interpolants step = (ro - lo) * (1.0f / (rx - lx)); + Interpolants o = lo + step * (startx + 0.5f - lx); + fragment_shader->init_span(&o, &step, 4.0f); + } + if (!use_discard) { + if (use_depth) { + if (span >= 8) { + if (ctx->depthfunc == GL_LEQUAL) { + if (ctx->depthmask) + draw_depth_span(z, buf, depth, span); + else + draw_depth_span(z, buf, depth, span); + } else { + if (ctx->depthmask) + draw_depth_span(z, buf, depth, span); + else + draw_depth_span(z, buf, depth, span); + } + buf += span & ~7; + depth += span & ~7; + span &= 7; + } + for (; span >= 4; span -= 4, buf += 4, depth += 4) { + commit_output(buf, z, depth); + } + if (span > 0) { + commit_output(buf, z, depth, span); + } + } else { + if (span >= 4) { + if (fragment_shader->has_draw_span(buf)) { + int len = span & ~3; + fragment_shader->draw_span(buf, len); + buf += len; + span &= 3; + } else { + do { + commit_output(buf); + buf += 4; + span -= 4; + } while (span >= 4); + } + } + if (span > 0) { + commit_output(buf, span); + } + } + } else { + if (use_depth) { + for (; span >= 4; span -= 4, buf += 4, depth += 4) { + commit_output(buf, z, depth); + } + if (span > 0) { + commit_output(buf, z, depth, span); + } + } else { + for (; span >= 4; span -= 4, buf += 4, depth += 4) { + commit_output(buf); + } + if (span > 0) { + commit_output(buf, span); + } + } + } + } + next_span: + lx += lm; + rx += rm; + y++; + lo += lom; + ro += rom; + fbuf += colortex.stride(sizeof(P)) / sizeof(P); + fdepth += depthtex.stride(sizeof(uint16_t)) / sizeof(uint16_t); + } +} + +static void draw_quad(int nump, Texture& colortex, int layer, + Texture& depthtex) { + Flats flat_outs; + Interpolants interp_outs[4] = {0}; + vertex_shader->run((char*)flat_outs, (char*)interp_outs, + sizeof(Interpolants)); + Float w = 1.0f / vertex_shader->gl_Position.w; + vec3 clip = vertex_shader->gl_Position.sel(X, Y, Z) * w; + vec3 screen = (clip + 1) * vec3(ctx->viewport.width / 2, + ctx->viewport.height / 2, 0.5f) + + vec3(ctx->viewport.x, ctx->viewport.y, 0); + Point p[4] = {{screen.x.x, screen.y.x}, + {screen.x.y, screen.y.y}, + {screen.x.z, screen.y.z}, + {screen.x.w, screen.y.w}}; + + auto top_left = min(min(p[0], p[1]), p[2]); + auto bot_right = max(max(p[0], p[1]), p[2]); + if (nump > 3) { + top_left = min(top_left, p[3]); + bot_right = max(bot_right, p[3]); + } + // debugf("bbox: %f %f %f %f\n", top_left.x, top_left.y, bot_right.x, + // bot_right.y); + + float fx0 = 0; + float fy0 = 0; + float fx1 = colortex.width; + float fy1 = colortex.height; + if (ctx->scissortest) { + fx0 = max(fx0, float(ctx->scissor.x)); + fy0 = max(fy0, float(ctx->scissor.y)); + fx1 = min(fx1, float(ctx->scissor.x + ctx->scissor.width)); + fy1 = min(fy1, float(ctx->scissor.y + ctx->scissor.height)); + } + + if (top_left.x >= fx1 || top_left.y >= fy1 || bot_right.x <= fx0 || + bot_right.y <= fy0) { + return; + } + + // SSE2 does not support unsigned comparison, so bias Z to be negative. + uint16_t z = uint16_t(0xFFFF * screen.z.x) - 0x8000; + fragment_shader->gl_FragCoordZW.x = screen.z.x; + fragment_shader->gl_FragCoordZW.y = w.x; + + fragment_shader->init_primitive(flat_outs); + + if (colortex.internal_format == GL_RGBA8) { + draw_quad_spans(nump, p, z, interp_outs, colortex, layer, + depthtex, fx0, fy0, fx1, fy1); + } else if (colortex.internal_format == GL_R8) { + draw_quad_spans(nump, p, z, interp_outs, colortex, layer, depthtex, + fx0, fy0, fx1, fy1); + } else { + assert(false); + } +} + +void VertexArray::validate() { + int last_enabled = -1; + for (int i = 0; i <= max_attrib; i++) { + if (attribs[i].enabled) { + VertexAttrib& attr = attribs[i]; + // VertexArray &v = ctx->vertex_arrays[attr.vertex_array]; + Buffer& vertex_buf = ctx->buffers[attr.vertex_buffer]; + attr.buf = vertex_buf.buf; + attr.buf_size = vertex_buf.size; + // debugf("%d %x %d %d %d %d\n", i, attr.type, attr.size, attr.stride, + // attr.offset, attr.divisor); + last_enabled = i; + } + } + max_attrib = last_enabled; +} + +extern "C" { + +void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, + void* indicesptr, GLsizei instancecount) { + assert(mode == GL_TRIANGLES || mode == GL_QUADS); + assert(type == GL_UNSIGNED_SHORT || type == GL_UNSIGNED_INT); + assert(count == 6); + assert(indicesptr == nullptr); + if (count <= 0 || instancecount <= 0 || indicesptr) { + return; + } + + Framebuffer& fb = *get_framebuffer(GL_DRAW_FRAMEBUFFER); + Texture& colortex = ctx->textures[fb.color_attachment]; + if (!colortex.buf) { + return; + } + assert(colortex.internal_format == GL_RGBA8 || + colortex.internal_format == GL_R8); + Texture& depthtex = ctx->textures[ctx->depthtest ? fb.depth_attachment : 0]; + if (depthtex.buf) { + assert(depthtex.internal_format == GL_DEPTH_COMPONENT16); + assert(colortex.width == depthtex.width && + colortex.height == depthtex.height); + } + + Buffer& indices_buf = ctx->buffers[ctx->element_array_buffer_binding]; + if (!indices_buf.buf) { + return; + } + + // debugf("current_vertex_array %d\n", ctx->current_vertex_array); + // debugf("indices size: %d\n", indices_buf.size); + VertexArray& v = ctx->vertex_arrays[ctx->current_vertex_array]; + if (ctx->validate_vertex_array) { + ctx->validate_vertex_array = false; + v.validate(); + } + +#ifndef NDEBUG + // uint64_t start = get_time_value(); +#endif + + ctx->shaded_rows = 0; + ctx->shaded_pixels = 0; + + uint16_t* indices = (uint16_t*)indices_buf.buf; + if (type == GL_UNSIGNED_INT) { + assert(indices_buf.size == size_t(count) * 4); + indices = new uint16_t[count]; + for (GLsizei i = 0; i < count; i++) { + uint32_t val = ((uint32_t*)indices_buf.buf)[i]; + assert(val <= 0xFFFFU); + indices[i] = val; + } + } else if (type == GL_UNSIGNED_SHORT) { + assert(indices_buf.size == size_t(count) * 2); + } else { + assert(0); + } + + vertex_shader->init_batch(program_impl); + fragment_shader->init_batch(program_impl); + if (count == 6 && indices[3] == indices[2] && indices[4] == indices[1]) { + uint16_t quad_indices[4] = {indices[0], indices[1], indices[5], indices[2]}; + vertex_shader->load_attribs(program_impl, v.attribs, quad_indices, 0, 0, 4); + // debugf("emulate quad %d %d %d %d\n", indices[0], indices[1], indices[5], + // indices[2]); + draw_quad(4, colortex, fb.layer, depthtex); + for (GLsizei instance = 1; instance < instancecount; instance++) { + vertex_shader->load_attribs(program_impl, v.attribs, nullptr, 0, instance, + 4); + draw_quad(4, colortex, fb.layer, depthtex); + } + } else { + for (GLsizei instance = 0; instance < instancecount; instance++) { + if (mode == GL_QUADS) + for (GLsizei i = 0; i + 4 <= count; i += 4) { + vertex_shader->load_attribs(program_impl, v.attribs, indices, i, + instance, 4); + // debugf("native quad %d %d %d %d\n", indices[i], indices[i+1], + // indices[i+2], indices[i+3]); + draw_quad(4, colortex, fb.layer, depthtex); + } + else + for (GLsizei i = 0; i + 3 <= count; i += 3) { + if (i + 6 <= count && indices[i + 3] == indices[i + 2] && + indices[i + 4] == indices[i + 1]) { + uint16_t quad_indices[4] = {indices[i], indices[i + 1], + indices[i + 5], indices[i + 2]}; + vertex_shader->load_attribs(program_impl, v.attribs, quad_indices, + 0, instance, 4); + // debugf("emulate quad %d %d %d %d\n", indices[i], indices[i+1], + // indices[i+5], indices[i+2]); + draw_quad(4, colortex, fb.layer, depthtex); + i += 3; + } else { + vertex_shader->load_attribs(program_impl, v.attribs, indices, i, + instance, 3); + // debugf("triangle %d %d %d %d\n", indices[i], indices[i+1], + // indices[i+2]); + draw_quad(3, colortex, fb.layer, depthtex); + } + } + } + } + + if (indices != (uint16_t*)indices_buf.buf) { + delete[] indices; + } + + if (ctx->samples_passed_query) { + Query& q = ctx->queries[ctx->samples_passed_query]; + q.value += ctx->shaded_pixels; + } + +#ifndef NDEBUG + // uint64_t end = get_time_value(); + // debugf("draw(%d): %fms for %d pixels in %d rows (avg %f pixels/row, %f + // ns/pixel)\n", instancecount, double(end - start)/(1000.*1000.), + // ctx->shaded_pixels, ctx->shaded_rows, + // double(ctx->shaded_pixels)/ctx->shaded_rows, double(end - + // start)/max(ctx->shaded_pixels, 1)); +#endif +} + +void Finish() {} + +void MakeCurrent(void* ctx_ptr) { + ctx = (Context*)ctx_ptr; + if (ctx) { + setup_program(ctx->current_program); + blend_key = ctx->blend ? ctx->blend_key : BLEND_KEY_NONE; + } else { + setup_program(0); + blend_key = BLEND_KEY_NONE; + } +} + +void* CreateContext() { return new Context; } + +void DestroyContext(void* ctx_ptr) { + if (!ctx_ptr) { + return; + } + if (ctx == ctx_ptr) { + MakeCurrent(nullptr); + } + delete (Context*)ctx_ptr; +} + +void Composite(GLuint srcId, GLint srcX, GLint srcY, GLsizei srcWidth, + GLsizei srcHeight, GLint dstX, GLint dstY, GLboolean opaque, + GLboolean flip) { + Framebuffer& fb = ctx->framebuffers[0]; + if (!fb.color_attachment) { + return; + } + Texture& srctex = ctx->textures[srcId]; + if (!srctex.buf) return; + prepare_texture(srctex); + Texture& dsttex = ctx->textures[fb.color_attachment]; + if (!dsttex.buf) return; + assert(srctex.bpp() == 4); + const int bpp = 4; + size_t src_stride = srctex.stride(bpp); + size_t dest_stride = dsttex.stride(bpp); + if (srcY < 0) { + dstY -= srcY; + srcHeight += srcY; + srcY = 0; + } + if (dstY < 0) { + srcY -= dstY; + srcHeight += dstY; + dstY = 0; + } + if (srcY + srcHeight > srctex.height) { + srcHeight = srctex.height - srcY; + } + if (dstY + srcHeight > dsttex.height) { + srcHeight = dsttex.height - dstY; + } + IntRect skip = {dstX, dstY, srcWidth, srcHeight}; + prepare_texture(dsttex, &skip); + char* dest = dsttex.buf + + (flip ? dsttex.height - 1 - dstY : dstY) * dest_stride + + dstX * bpp; + char* src = srctex.buf + srcY * src_stride + srcX * bpp; + if (flip) { + dest_stride = -dest_stride; + } + if (opaque) { + for (int y = 0; y < srcHeight; y++) { + memcpy(dest, src, srcWidth * bpp); + dest += dest_stride; + src += src_stride; + } + } else { + for (int y = 0; y < srcHeight; y++) { + char* end = src + srcWidth * bpp; + while (src + 4 * bpp <= end) { + WideRGBA8 srcpx = unpack(unaligned_load(src)); + WideRGBA8 dstpx = unpack(unaligned_load(dest)); + PackedRGBA8 r = pack(srcpx + dstpx - muldiv255(dstpx, alphas(srcpx))); + unaligned_store(dest, r); + src += 4 * bpp; + dest += 4 * bpp; + } + if (src < end) { + WideRGBA8 srcpx = unpack(unaligned_load(src)); + WideRGBA8 dstpx = unpack(unaligned_load(dest)); + U32 r = bit_cast( + pack(srcpx + dstpx - muldiv255(dstpx, alphas(srcpx)))); + unaligned_store(dest, r.x); + if (src + bpp < end) { + unaligned_store(dest + bpp, r.y); + if (src + 2 * bpp < end) { + unaligned_store(dest + 2 * bpp, r.z); + } + } + dest += end - src; + src = end; + } + dest += dest_stride - srcWidth * bpp; + src += src_stride - srcWidth * bpp; + } + } +} + +} // extern "C" diff --git a/swgl/src/gl_defs.h b/swgl/src/gl_defs.h new file mode 100644 index 0000000000..3cd774e49b --- /dev/null +++ b/swgl/src/gl_defs.h @@ -0,0 +1,175 @@ +/* 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/. */ + +typedef int8_t GLbyte; +typedef uint8_t GLubyte; +typedef int16_t GLshort; +typedef uint16_t GLushort; +typedef int32_t GLint; +typedef uint32_t GLuint; +typedef int64_t GLint64; +typedef uint64_t GLuint64; + +typedef float GLfloat; +typedef double GLdouble; + +typedef uint32_t GLenum; +typedef int32_t GLboolean; +typedef uint32_t GLbitfield; + +typedef int32_t GLsizei; +typedef size_t GLsizeiptr; +typedef intptr_t GLintptr; + +#define GL_NO_ERROR 0 + +#define GL_RGBA32F 0x8814 +#define GL_RGBA8 0x8058 +#define GL_R8 0x8229 +#define GL_RGBA32I 0x8D82 +#define GL_BGRA8 0x93A1 + +#define GL_BYTE 0x1400 +#define GL_UNSIGNED_BYTE 0x1401 +#define GL_SHORT 0x1402 +#define GL_UNSIGNED_SHORT 0x1403 +#define GL_INT 0x1404 +#define GL_UNSIGNED_INT 0x1405 +#define GL_FLOAT 0x1406 + +#define GL_RED 0x1903 +#define GL_GREEN 0x1904 +#define GL_BLUE 0x1905 +#define GL_ALPHA 0x1906 +#define GL_RGB 0x1907 +#define GL_RGBA 0x1908 +#define GL_RGBA_INTEGER 0x8D99 +#define GL_BGRA 0x80E1 + +#define GL_DEPTH_COMPONENT 0x1902 +#define GL_DEPTH_COMPONENT16 0x81A5 +#define GL_DEPTH_COMPONENT24 0x81A6 +#define GL_DEPTH_COMPONENT32 0x81A7 + +#define GL_ARRAY_BUFFER 0x8892 +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 + +#define GL_READ_FRAMEBUFFER 0x8CA8 +#define GL_DRAW_FRAMEBUFFER 0x8CA9 +#define GL_FRAMEBUFFER 0x8D40 +#define GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_READ_FRAMEBUFFER_BINDING 0x8CAA +#define GL_RENDERBUFFER 0x8D41 +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#define GL_DEPTH_ATTACHMENT 0x8D00 +#define GL_STENCIL_ATTACHMENT 0x8D20 +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB +#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC +#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD +#define GL_COLOR_BUFFER_BIT 0x00004000 +#define GL_DEPTH_BUFFER_BIT 0x00000100 +#define GL_STENCIL_BUFFER_BIT 0x00000400 + +#define GL_PIXEL_PACK_BUFFER 0x88EB +#define GL_PIXEL_UNPACK_BUFFER 0x88EC +#define GL_PIXEL_PACK_BUFFER_BINDING 0x88ED +#define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF +#define GL_UNPACK_ROW_LENGTH 0x0CF2 +#define GL_UNPACK_ALIGNMENT 0x0CF5 + +#define GL_QUERY_RESULT 0x8866 +#define GL_QUERY_RESULT_AVAILABLE 0x8867 +#define GL_TIME_ELAPSED 0x88BF +#define GL_SAMPLES_PASSED 0x8914 + +#define GL_NEAREST 0x2600 +#define GL_LINEAR 0x2601 +#define GL_NEAREST_MIPMAP_NEAREST 0x2700 +#define GL_NEAREST_MIPMAP_LINEAR 0x2702 +#define GL_LINEAR_MIPMAP_NEAREST 0x2701 +#define GL_LINEAR_MIPMAP_LINEAR 0x2703 +#define GL_TEXTURE_WRAP_S 0x2802 +#define GL_TEXTURE_WRAP_T 0x2803 +#define GL_TEXTURE_MAG_FILTER 0x2800 +#define GL_TEXTURE_MIN_FILTER 0x2801 +#define GL_CLAMP_TO_EDGE 0x812F +#define GL_TEXTURE_2D 0x0DE1 +#define GL_TEXTURE_3D 0x806F +#define GL_TEXTURE_2D_ARRAY 0x8C1A +#define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE1 0x84C1 +#define GL_TEXTURE2 0x84C2 +#define GL_TEXTURE3 0x84C3 +#define GL_TEXTURE4 0x84C4 +#define GL_TEXTURE5 0x84C5 +#define GL_TEXTURE6 0x84C6 +#define GL_TEXTURE7 0x84C7 +#define GL_TEXTURE8 0x84C8 +#define GL_TEXTURE9 0x84C9 +#define GL_TEXTURE10 0x84CA +#define GL_TEXTURE11 0x84CB +#define GL_TEXTURE12 0x84CC +#define GL_TEXTURE13 0x84CD +#define GL_TEXTURE14 0x84CE +#define GL_TEXTURE15 0x84CF +#define GL_MAX_TEXTURE_UNITS 0x84E2 +#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 +#define GL_MAX_TEXTURE_SIZE 0x0D33 +#define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF + +#define GL_VERTEX_SHADER 0x8B31 +#define GL_FRAGMENT_SHADER 0x8B30 + +#define GL_BLEND 0x0BE2 +#define GL_ZERO 0 +#define GL_ONE 1 +#define GL_SRC_COLOR 0x0300 +#define GL_ONE_MINUS_SRC_COLOR 0x0301 +#define GL_SRC_ALPHA 0x0302 +#define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#define GL_DST_ALPHA 0x0304 +#define GL_ONE_MINUS_DST_ALPHA 0x0305 +#define GL_DST_COLOR 0x0306 +#define GL_ONE_MINUS_DST_COLOR 0x0307 +#define GL_CONSTANT_COLOR 0x8001 +#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 +#define GL_CONSTANT_ALPHA 0x8003 +#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 +#define GL_SRC1_ALPHA 0x8589 +#define GL_SRC1_COLOR 0x88F9 +#define GL_ONE_MINUS_SRC1_COLOR 0x88FA +#define GL_ONE_MINUS_SRC1_ALPHA 0x88FB + +#define GL_FUNC_ADD 0x8006 + +#define GL_NEVER 0x0200 +#define GL_LESS 0x0201 +#define GL_EQUAL 0x0202 +#define GL_LEQUAL 0x0203 +#define GL_GREATER 0x0204 +#define GL_NOTEQUAL 0x0205 +#define GL_GEQUAL 0x0206 +#define GL_ALWAYS 0x0207 +#define GL_DEPTH_TEST 0x0B71 +#define GL_DEPTH_WRITEMASK 0x0B72 + +#define GL_SCISSOR_TEST 0x0C11 + +#define GL_VENDOR 0x1F00 +#define GL_RENDERER 0x1F01 +#define GL_VERSION 0x1F02 +#define GL_EXTENSIONS 0x1F03 +#define GL_NUM_EXTENSIONS 0x821D + +#define GL_POINTS 0x0000 +#define GL_LINES 0x0001 +#define GL_LINE_LOOP 0x0002 +#define GL_LINE_STRIP 0x0003 +#define GL_TRIANGLES 0x0004 +#define GL_TRIANGLE_STRIP 0x0005 +#define GL_TRIANGLE_FAN 0x0006 +#define GL_QUADS 0x0007 diff --git a/swgl/src/glsl.h b/swgl/src/glsl.h new file mode 100644 index 0000000000..cc94aa3d2d --- /dev/null +++ b/swgl/src/glsl.h @@ -0,0 +1,3053 @@ +/* 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/. */ + +// Some of this is copied from Skia and is governed by a BSD-style license +// Every function in this file should be marked static and inline using SI. +#define SI ALWAYS_INLINE static + +#include "vector_type.h" + +namespace glsl { + +float make_float(float n) { return n; } + +float make_float(int32_t n) { return float(n); } + +float make_float(uint32_t n) { return float(n); } + +float make_float(bool n) { return float(n); } + +template +Float make_float(T v) { + return CONVERT(v, Float); +} + +int32_t make_int(uint32_t n) { return n; } + +int32_t make_int(int32_t n) { return n; } + +int32_t make_int(float n) { return int32_t(n); } + +int32_t make_int(bool n) { return int32_t(n); } + +template +I32 make_int(T v) { + return CONVERT(v, I32); +} + +uint32_t make_uint(uint32_t n) { return n; } + +uint32_t make_uint(int32_t n) { return n; } + +uint32_t make_uint(float n) { return uint32_t(n); } + +uint32_t make_uint(bool n) { return uint32_t(n); } + +template +U32 make_uint(T v) { + return CONVERT(v, U32); +} + +template +T force_scalar(T n) { + return n; +} + +float force_scalar(Float f) { return f[0]; } + +int32_t force_scalar(I32 i) { return i[0]; } + +struct vec4; +struct ivec2; + +SI int32_t if_then_else(int32_t c, int32_t t, int32_t e) { return c ? t : e; } + +SI float if_then_else(int32_t c, float t, float e) { return c ? t : e; } + +SI Float if_then_else(I32 c, float t, float e) { + return Float((c & I32(Float(t))) | (~c & I32(Float(e)))); +} + +SI I32 if_then_else(I32 c, int32_t t, int32_t e) { + return (c & I32(t)) | (~c & I32(e)); +} + +SI Float if_then_else(I32 c, Float t, Float e) { + return Float((c & I32(t)) | (~c & I32(e))); +} + +SI Float if_then_else(int32_t c, Float t, Float e) { return c ? t : e; } + +SI Bool if_then_else(I32 c, Bool t, Bool e) { return (c & t) | (~c & e); } + +SI Bool if_then_else(int32_t c, Bool t, Bool e) { return c ? t : e; } + +SI int32_t min(int32_t a, int32_t b) { return a < b ? a : b; } +SI int32_t max(int32_t a, int32_t b) { return a > b ? a : b; } + +SI int32_t clamp(int32_t a, int32_t minVal, int32_t maxVal) { + return min(max(a, minVal), maxVal); +} + +SI float min(float a, float b) { return a < b ? a : b; } +SI float max(float a, float b) { return a > b ? a : b; } + +SI float clamp(float a, float minVal, float maxVal) { + return min(max(a, minVal), maxVal); +} + +SI Float min(Float a, Float b) { +#if USE_SSE2 + return _mm_min_ps(a, b); +#elif USE_NEON + return vminq_f32(a, b); +#else + return if_then_else(a < b, a, b); +#endif +} + +SI Float max(Float a, Float b) { +#if USE_SSE2 + return _mm_max_ps(a, b); +#elif USE_NEON + return vmaxq_f32(a, b); +#else + return if_then_else(a > b, a, b); +#endif +} + +SI Float clamp(Float a, Float minVal, Float maxVal) { + return min(max(a, minVal), maxVal); +} + +#define sqrt __glsl_sqrt + +SI float sqrt(float x) { return sqrtf(x); } + +SI Float sqrt(Float v) { +#if USE_SSE2 + return _mm_sqrt_ps(v); +#elif USE_NEON + Float e = vrsqrteq_f32(v); + e *= vrsqrtsq_f32(v, e * e); + e *= vrsqrtsq_f32(v, e * e); + return v * e; +#else + return (Float){sqrtf(v.x), sqrtf(v.y), sqrtf(v.z), sqrtf(v.w)}; +#endif +} + +SI float inversesqrt(float x) { return 1.0f / sqrtf(x); } + +SI Float inversesqrt(Float v) { +#if USE_SSE2 + return _mm_rsqrt_ps(v); +#elif USE_NEON + Float e = vrsqrteq_f32(v); + return vrsqrtsq_f32(v, e * e) * e; +#else + return 1.0f / sqrt(v); +#endif +} + +SI float step(float edge, float x) { return float(x >= edge); } + +SI Float step(Float edge, Float x) { + return if_then_else(x < edge, Float(0), Float(1)); +} + +/* +enum RGBA { + R, + G, + B, + A +};*/ + +enum XYZW { + X = 0, + Y = 1, + Z = 2, + W = 3, + R = 0, + G = 1, + B = 2, + A = 3, +}; + +struct bvec2_scalar { + bool x; + bool y; + + bvec2_scalar() : bvec2_scalar(false) {} + constexpr bvec2_scalar(bool a) : x(a), y(a) {} + constexpr bvec2_scalar(bool x, bool y) : x(x), y(y) {} +}; + +struct bvec2 { + bvec2() : bvec2(0) {} + bvec2(Bool a) : x(a), y(a) {} + bvec2(Bool x, Bool y) : x(x), y(y) {} + Bool& select(XYZW c) { + switch (c) { + case X: + return x; + case Y: + return y; + default: + UNREACHABLE; + } + } + Bool sel(XYZW c1) { return select(c1); } + + bvec2 operator~() { return bvec2(~x, ~y); } + + Bool x; + Bool y; +}; + +bvec2_scalar make_bvec2(bool n) { return bvec2_scalar{n, n}; } + +bvec2_scalar make_bvec2(bool x, bool y) { return bvec2_scalar{x, y}; } + +template +bvec2 make_bvec2(const N& n) { + return bvec2(n); +} + +template +bvec2 make_bvec2(const X& x, const Y& y) { + return bvec2(x, y); +} + +struct vec4_scalar; + +struct vec2_scalar { + typedef struct vec2 vector_type; + typedef float element_type; + + float x; + float y; + + constexpr vec2_scalar() : vec2_scalar(0.0f) {} + constexpr vec2_scalar(float a) : x(a), y(a) {} + constexpr vec2_scalar(int a) : x(a), y(a) {} + constexpr vec2_scalar(float x, float y) : x(x), y(y) {} + + float& select(XYZW c) { + switch (c) { + case X: + return x; + case Y: + return y; + default: + UNREACHABLE; + } + } + float& sel(XYZW c1) { return select(c1); } + vec2_scalar sel(XYZW c1, XYZW c2) { + return vec2_scalar(select(c1), select(c2)); + } + vec4_scalar sel(XYZW c1, XYZW c2, XYZW c3, XYZW c4); + + friend bool operator==(const vec2_scalar& l, const vec2_scalar& r) { + return l.x == r.x && l.y == r.y; + } + + friend bool operator!=(const vec2_scalar& l, const vec2_scalar& r) { + return l.x != r.x || l.y != r.y; + } + + friend vec2_scalar operator*(float a, vec2_scalar b) { + return vec2_scalar(a * b.x, a * b.y); + } + friend vec2_scalar operator*(vec2_scalar a, float b) { + return vec2_scalar(a.x * b, a.y * b); + } + friend vec2_scalar operator*(vec2_scalar a, vec2_scalar b) { + return vec2_scalar(a.x * b.x, a.y * b.y); + } + friend vec2_scalar operator/(vec2_scalar a, vec2_scalar b) { + return vec2_scalar(a.x / b.x, a.y / b.y); + } + + friend vec2_scalar operator-(vec2_scalar a, vec2_scalar b) { + return vec2_scalar(a.x - b.x, a.y - b.y); + } + friend vec2_scalar operator+(vec2_scalar a, vec2_scalar b) { + return vec2_scalar(a.x + b.x, a.y + b.y); + } + friend vec2_scalar operator+(vec2_scalar a, float b) { + return vec2_scalar(a.x + b, a.y + b); + } + + vec2_scalar operator-() { return vec2_scalar(-x, -y); } + + vec2_scalar operator+=(vec2_scalar a) { + x += a.x; + y += a.y; + return *this; + } + + vec2_scalar operator-=(vec2_scalar a) { + x -= a.x; + y -= a.y; + return *this; + } +}; + +struct vec2_scalar_ref { + vec2_scalar_ref(float& x, float& y) : x(x), y(y) {} + float& x; + float& y; + + float& select(XYZW c) { + switch (c) { + case X: + return x; + case Y: + return y; + default: + UNREACHABLE; + } + } + float& sel(XYZW c1) { return select(c1); } + + vec2_scalar_ref& operator=(const vec2_scalar& a) { + x = a.x; + y = a.y; + return *this; + } + vec2_scalar_ref& operator*=(vec2_scalar a) { + x *= a.x; + y *= a.y; + return *this; + } + operator vec2_scalar() const { return vec2_scalar{x, y}; } +}; + +struct vec2 { + typedef struct vec2 vector_type; + typedef float element_type; + + constexpr vec2() : vec2(Float(0.0f)) {} + constexpr vec2(Float a) : x(a), y(a) {} + vec2(Float x, Float y) : x(x), y(y) {} + constexpr vec2(vec2_scalar s) : x(s.x), y(s.y) {} + constexpr vec2(vec2_scalar s0, vec2_scalar s1, vec2_scalar s2, vec2_scalar s3) + : x(Float{s0.x, s1.x, s2.x, s3.x}), y(Float{s0.y, s1.y, s2.y, s3.y}) {} + vec2(ivec2 a); + Float x; + Float y; + + Float& select(XYZW c) { + switch (c) { + case X: + return x; + case Y: + return y; + default: + UNREACHABLE; + } + } + Float& sel(XYZW c1) { return select(c1); } + vec2 sel(XYZW c1, XYZW c2) { return vec2(select(c1), select(c2)); } + + vec4 sel(XYZW c1, XYZW c2, XYZW c3, XYZW c4); + + vec2 operator*=(Float a) { + x *= a; + y *= a; + return *this; + } + vec2 operator*=(vec2 a) { + x *= a.x; + y *= a.y; + return *this; + } + + vec2 operator/=(Float a) { + x /= a; + y /= a; + return *this; + } + vec2 operator/=(vec2 a) { + x /= a.x; + y /= a.y; + return *this; + } + + vec2 operator+=(vec2 a) { + x += a.x; + y += a.y; + return *this; + } + vec2 operator-=(vec2 a) { + x -= a.x; + y -= a.y; + return *this; + } + vec2 operator-=(Float a) { + x -= a; + y -= a; + return *this; + } + + vec2 operator-() { return vec2(-x, -y); } + + friend I32 operator==(const vec2& l, const vec2& r) { + return l.x == r.x && l.y == r.y; + } + + friend I32 operator!=(const vec2& l, const vec2& r) { + return l.x != r.x || l.y != r.y; + } + + friend vec2 operator*(vec2 a, Float b) { return vec2(a.x * b, a.y * b); } + friend vec2 operator*(vec2 a, vec2 b) { return vec2(a.x * b.x, a.y * b.y); } + friend vec2 operator*(Float a, vec2 b) { return vec2(a * b.x, a * b.y); } + + friend vec2 operator/(vec2 a, vec2 b) { return vec2(a.x / b.x, a.y / b.y); } + friend vec2 operator/(vec2 a, Float b) { return vec2(a.x / b, a.y / b); } + + friend vec2 operator-(vec2 a, vec2 b) { return vec2(a.x - b.x, a.y - b.y); } + friend vec2 operator-(vec2 a, Float b) { return vec2(a.x - b, a.y - b); } + friend vec2 operator-(Float a, vec2 b) { return vec2(a - b.x, a - b.y); } + friend vec2 operator+(vec2 a, vec2 b) { return vec2(a.x + b.x, a.y + b.y); } + friend vec2 operator+(vec2 a, Float b) { return vec2(a.x + b, a.y + b); } + friend vec2 operator+(Float a, vec2 b) { return vec2(a + b.x, a + b.y); } +}; + +vec2_scalar force_scalar(const vec2& v) { + return vec2_scalar{force_scalar(v.x), force_scalar(v.y)}; +} + +vec2_scalar make_vec2(float n) { return vec2_scalar{n, n}; } + +vec2_scalar make_vec2(float x, float y) { return vec2_scalar{x, y}; } + +template +vec2 make_vec2(const N& n) { + return vec2(n); +} + +template +vec2 make_vec2(const X& x, const Y& y) { + return vec2(x, y); +} + +vec2 operator*(vec2_scalar a, Float b) { return vec2(a.x * b, a.y * b); } + +vec2 operator*(Float a, vec2_scalar b) { return vec2(a * b.x, a * b.y); } + +SI vec2 min(vec2 a, vec2 b) { return vec2(min(a.x, b.x), min(a.y, b.y)); } + +SI vec2_scalar min(vec2_scalar a, vec2_scalar b) { + return vec2_scalar{min(a.x, b.x), min(a.y, b.y)}; +} + +SI vec2 if_then_else(I32 c, vec2 t, vec2 e) { + return vec2(if_then_else(c, t.x, e.x), if_then_else(c, t.y, e.y)); +} + +SI vec2 if_then_else(int32_t c, vec2 t, vec2 e) { return c ? t : e; } + +vec2 step(vec2 edge, vec2 x) { + return vec2(step(edge.x, x.x), step(edge.y, x.y)); +} + +vec2 max(vec2 a, vec2 b) { return vec2(max(a.x, b.x), max(a.y, b.y)); } +vec2 max(vec2 a, Float b) { return vec2(max(a.x, b), max(a.y, b)); } + +SI vec2_scalar max(vec2_scalar a, vec2_scalar b) { + return vec2_scalar{max(a.x, b.x), max(a.y, b.y)}; +} +SI vec2_scalar max(vec2_scalar a, float b) { + return vec2_scalar{max(a.x, b), max(a.y, b)}; +} + +Float length(vec2 a) { return sqrt(a.x * a.x + a.y * a.y); } + +float length(vec2_scalar a) { return hypotf(a.x, a.y); } + +SI Float distance(vec2 a, vec2 b) { return length(a - b); } + +SI vec2 normalize(vec2 a) { return a / length(a); } + +#define abs __glsl_abs + +int32_t abs(int32_t a) { return a < 0 ? -a : a; } + +float abs(float a) { return fabsf(a); } + +Float abs(Float v) { +#if USE_NEON + return vabsq_f32(v); +#else + return Float(I32(v) & I32(0.0f - v)); +#endif +} + +Float cast(U32 v) { return CONVERT((I32)v, Float); } +Float cast(I32 v) { return CONVERT((I32)v, Float); } +I32 cast(Float v) { return CONVERT(v, I32); } + +#define floor __glsl_floor + +float floor(float a) { return floorf(a); } + +Float floor(Float v) { + Float roundtrip = cast(cast(v)); + return roundtrip - if_then_else(roundtrip > v, Float(1), Float(0)); +} + +vec2 floor(vec2 v) { return vec2(floor(v.x), floor(v.y)); } + +vec2_scalar floor(vec2_scalar v) { + return vec2_scalar{floorf(v.x), floorf(v.y)}; +} + +#define ceil __glsl_ceil + +float ceil(float a) { return ceilf(a); } + +Float ceil(Float v) { + Float roundtrip = cast(cast(v)); + return roundtrip + if_then_else(roundtrip < v, Float(1), Float(0)); +} + +SI int32_t roundto(float v, float scale) { return int32_t(v * scale + 0.5f); } + +SI I32 roundto(Float v, Float scale) { +#if USE_SSE2 + return _mm_cvtps_epi32(v * scale); +#else + return cast(v * scale + 0.5f); +#endif +} + +#define round __glsl_round + +float round(float a) { return roundf(a); } + +float fract(float a) { return a - floor(a); } + +Float round(Float v) { return floor(v + 0.5f); } + +Float fract(Float v) { return v - floor(v); } + +// X derivatives can be approximated by dFdx(x) = x[1] - x[0]. +// Y derivatives are not easily available since we operate in terms of X spans +// only. To work around, assume dFdy(p.x) = dFdx(p.y), which only holds for +// uniform scaling, and thus abs(dFdx(p.x)) + abs(dFdy(p.x)) = abs(dFdx(p.x)) + +// abs(dFdx(p.y)) which mirrors abs(dFdx(p.y)) + abs(dFdy(p.y)) = abs(dFdx(p.y)) +// + abs(dFdx(p.x)). +vec2 fwidth(vec2 p) { + Float d = abs(SHUFFLE(p.x, p.y, 1, 1, 5, 5) - SHUFFLE(p.x, p.y, 0, 0, 4, 4)); + return vec2(d.xyxy + d.zwzw); +} + +// See +// http://www.machinedlearnings.com/2011/06/fast-approximate-logarithm-exponential.html. +Float approx_log2(Float x) { + // e - 127 is a fair approximation of log2(x) in its own right... + Float e = cast(bit_cast(x)) * (1.0f / (1 << 23)); + + // ... but using the mantissa to refine its error is _much_ better. + Float m = bit_cast((bit_cast(x) & 0x007fffff) | 0x3f000000); + return e - 124.225514990f - 1.498030302f * m - + 1.725879990f / (0.3520887068f + m); +} +Float approx_pow2(Float x) { + Float f = fract(x); + return bit_cast( + roundto(1.0f * (1 << 23), x + 121.274057500f - 1.490129070f * f + + 27.728023300f / (4.84252568f - f))); +} + +// From skia +Float pow(Float x, Float y) { + return if_then_else((x == 0) | (x == 1), x, approx_pow2(approx_log2(x) * y)); +} + +Float exp(Float y) { + float x = 2.718281828459045235360287471352; + return approx_pow2(log2f(x) * y); +} + +struct ivec4; + +struct ivec2_scalar { + typedef int32_t element_type; + + int32_t x; + int32_t y; + + ivec2_scalar() : ivec2_scalar(0) {} + constexpr ivec2_scalar(int32_t a) : x(a), y(a) {} + constexpr ivec2_scalar(int32_t x, int32_t y) : x(x), y(y) {} + + int32_t& select(XYZW c) { + switch (c) { + case X: + return x; + case Y: + return y; + default: + UNREACHABLE; + } + } + int32_t& sel(XYZW c1) { return select(c1); } + ivec2_scalar sel(XYZW c1, XYZW c2) { + return ivec2_scalar{select(c1), select(c2)}; + } + + ivec2_scalar& operator+=(ivec2_scalar a) { + x += a.x; + y += a.y; + return *this; + } + ivec2_scalar& operator+=(int n) { + x += n; + y += n; + return *this; + } + + ivec2_scalar& operator>>=(int shift) { + x >>= shift; + y >>= shift; + return *this; + } + + friend ivec2_scalar operator&(ivec2_scalar a, int b) { + return ivec2_scalar{a.x & b, a.y & b}; + } + + friend ivec2_scalar operator+(ivec2_scalar a, ivec2_scalar b) { + return ivec2_scalar{a.x + b.x, a.y + b.y}; + } +}; + +struct ivec2 { + typedef int32_t element_type; + + ivec2() : ivec2(I32(0)) {} + ivec2(I32 a) : x(a), y(a) {} + ivec2(I32 x, I32 y) : x(x), y(y) {} + ivec2(vec2 a) : x(cast(a.x)), y(cast(a.y)) {} + ivec2(U32 x, U32 y) : x(CONVERT(x, I32)), y(CONVERT(y, I32)) {} + constexpr ivec2(ivec2_scalar s) : x(s.x), y(s.y) {} + constexpr ivec2(ivec2_scalar s0, ivec2_scalar s1, ivec2_scalar s2, + ivec2_scalar s3) + : x(I32{s0.x, s1.x, s2.x, s3.x}), y(I32{s0.y, s1.y, s2.y, s3.y}) {} + I32 x; + I32 y; + + I32& select(XYZW c) { + switch (c) { + case X: + return x; + case Y: + return y; + default: + UNREACHABLE; + } + } + I32& sel(XYZW c1) { return select(c1); } + + ivec2 sel(XYZW c1, XYZW c2) { return ivec2(select(c1), select(c2)); } + + ivec4 sel(XYZW c1, XYZW c2, XYZW c3, XYZW c4); + + ivec2& operator*=(I32 a) { + x *= a; + y *= a; + return *this; + } + ivec2& operator+=(ivec2 a) { + x += a.x; + y += a.y; + return *this; + } + ivec2& operator>>=(int shift) { + x >>= shift; + y >>= shift; + return *this; + } + + friend ivec2 operator*(ivec2 a, I32 b) { return ivec2(a.x * b, a.y * b); } + friend ivec2 operator&(ivec2 a, ivec2 b) { + return ivec2(a.x & b.x, a.y & b.y); + } + friend ivec2 operator&(ivec2 a, I32 b) { return ivec2(a.x & b, a.y & b); } + friend ivec2 operator+(ivec2 a, ivec2 b) { + return ivec2(a.x + b.x, a.y + b.y); + } +}; + +vec2::vec2(ivec2 a) : x(cast(a.x)), y(cast(a.y)) {} + +ivec2_scalar make_ivec2(int32_t n) { return ivec2_scalar{n, n}; } + +ivec2_scalar make_ivec2(uint32_t n) { + return ivec2_scalar{int32_t(n), int32_t(n)}; +} + +ivec2_scalar make_ivec2(int32_t x, int32_t y) { return ivec2_scalar{x, y}; } + +ivec2_scalar make_ivec2(uint32_t x, uint32_t y) { + return ivec2_scalar{int32_t(x), int32_t(y)}; +} + +vec2_scalar make_vec2(const ivec2_scalar& v) { + return vec2_scalar{float(v.x), float(v.y)}; +} + +ivec2_scalar make_ivec2(const vec2_scalar& v) { + return ivec2_scalar{int32_t(v.x), int32_t(v.y)}; +} + +template +ivec2 make_ivec2(const N& n) { + return ivec2(n); +} + +template +ivec2 make_ivec2(const X& x, const Y& y) { + return ivec2(x, y); +} + +ivec2_scalar force_scalar(const ivec2& v) { + return ivec2_scalar{force_scalar(v.x), force_scalar(v.y)}; +} + +struct ivec3_scalar { + int32_t x; + int32_t y; + int32_t z; + + ivec3_scalar() : ivec3_scalar(0) {} + constexpr ivec3_scalar(int32_t a) : x(a), y(a), z(a) {} + constexpr ivec3_scalar(int32_t x, int32_t y, int32_t z) : x(x), y(y), z(z) {} + + int32_t& select(XYZW c) { + switch (c) { + case X: + return x; + case Y: + return y; + case Z: + return z; + default: + UNREACHABLE; + } + } + int32_t& sel(XYZW c1) { return select(c1); } + ivec2_scalar sel(XYZW c1, XYZW c2) { + return ivec2_scalar{select(c1), select(c2)}; + } +}; + +struct ivec3 { + ivec3() : ivec3(0) {} + ivec3(I32 a) : x(a), y(a), z(a) {} + ivec3(I32 x, I32 y, I32 z) : x(x), y(y), z(z) {} + ivec3(ivec2 a, I32 b) : x(a.x), y(a.y), z(b) {} + ivec3(vec2 a, Float b) : x(cast(a.x)), y(cast(a.y)), z(cast(b)) {} + I32 x; + I32 y; + I32 z; + + friend ivec3 operator+(ivec3 a, ivec3 b) { + return ivec3(a.x + b.x, a.y + b.y, a.z + b.z); + } +}; + +vec2_scalar make_vec2(ivec3_scalar s) { + return vec2_scalar{float(s.x), float(s.y)}; +} + +ivec3_scalar make_ivec3(int32_t n) { return ivec3_scalar{n, n, n}; } + +ivec3_scalar make_ivec3(const ivec2_scalar& v, int32_t z) { + return ivec3_scalar{v.x, v.y, z}; +} + +ivec3_scalar make_ivec3(int32_t x, int32_t y, int32_t z) { + return ivec3_scalar{x, y, z}; +} + +template +ivec3 make_ivec3(const N& n) { + return ivec3(n); +} + +template +ivec3 make_ivec3(const X& x, const Y& y) { + return ivec3(x, y); +} + +template +ivec3 make_ivec3(const X& x, const Y& y, const Z& z) { + return ivec3(x, y, z); +} + +struct ivec4_scalar { + typedef int32_t element_type; + + int32_t x; + int32_t y; + int32_t z; + int32_t w; + + ivec4_scalar() : ivec4_scalar(0) {} + constexpr ivec4_scalar(int32_t a) : x(a), y(a), z(a), w(a) {} + constexpr ivec4_scalar(int32_t x, int32_t y, int32_t z, int32_t w) + : x(x), y(y), z(z), w(w) {} + + int32_t& select(XYZW c) { + switch (c) { + case X: + return x; + case Y: + return y; + case Z: + return z; + case W: + return w; + default: + UNREACHABLE; + } + } + int32_t& sel(XYZW c1) { return select(c1); } + ivec2_scalar sel(XYZW c1, XYZW c2) { + return ivec2_scalar{select(c1), select(c2)}; + } + + friend ivec4_scalar operator&(int32_t a, ivec4_scalar b) { + return ivec4_scalar{a & b.x, a & b.y, a & b.z, a & b.w}; + } +}; + +struct ivec4 { + typedef int32_t element_type; + + ivec4() : ivec4(I32(0)) {} + ivec4(I32 a) : x(a), y(a), z(a), w(a) {} + ivec4(I32 x, I32 y, I32 z, I32 w) : x(x), y(y), z(z), w(w) {} + ivec4(ivec2 a, I32 b, I32 c) : x(a.x), y(a.y), z(b), w(c) {} + constexpr ivec4(ivec4_scalar s) : x(s.x), y(s.y), z(s.z), w(s.w) {} + constexpr ivec4(ivec4_scalar s0, ivec4_scalar s1, ivec4_scalar s2, + ivec4_scalar s3) + : x(I32{s0.x, s1.x, s2.x, s3.x}), + y(I32{s0.y, s1.y, s2.y, s3.y}), + z(I32{s0.z, s1.z, s2.z, s3.z}), + w(I32{s0.w, s1.w, s2.w, s3.w}) {} + + I32& select(XYZW c) { + switch (c) { + case X: + return x; + case Y: + return y; + case Z: + return z; + case W: + return w; + default: + UNREACHABLE; + } + } + I32 sel(XYZW c1) { return select(c1); } + + ivec2 sel(XYZW c1, XYZW c2) { return ivec2(select(c1), select(c2)); } + + ivec3 sel(XYZW c1, XYZW c2, XYZW c3) { + return ivec3(select(c1), select(c2), select(c3)); + } + + friend ivec4 operator&(I32 a, ivec4 b) { + return ivec4(a & b.x, a & b.y, a & b.z, a & b.w); + } + + I32 x; + I32 y; + I32 z; + I32 w; +}; + +ivec4_scalar force_scalar(const ivec4& v) { + return ivec4_scalar{force_scalar(v.x), force_scalar(v.y), force_scalar(v.z), + force_scalar(v.w)}; +} + +ivec4_scalar make_ivec4(int32_t n) { return ivec4_scalar{n, n, n, n}; } + +ivec4_scalar make_ivec4(const ivec2_scalar& xy, int32_t z, int32_t w) { + return ivec4_scalar{xy.x, xy.y, z, w}; +} + +ivec4_scalar make_ivec4(int32_t x, int32_t y, int32_t z, int32_t w) { + return ivec4_scalar{x, y, z, w}; +} + +template +ivec4 make_ivec4(const N& n) { + return ivec4(n); +} + +template +ivec4 make_ivec4(const X& x, const Y& y, const Z& z) { + return ivec4(x, y, z); +} + +template +ivec4 make_ivec4(const X& x, const Y& y, const Z& z, const W& w) { + return ivec4(x, y, z, w); +} + +SI ivec2 if_then_else(I32 c, ivec2 t, ivec2 e) { + return ivec2(if_then_else(c, t.x, e.x), if_then_else(c, t.y, e.y)); +} + +SI ivec2 if_then_else(int32_t c, ivec2 t, ivec2 e) { return c ? t : e; } + +SI ivec4 if_then_else(I32 c, ivec4 t, ivec4 e) { + return ivec4(if_then_else(c, t.x, e.x), if_then_else(c, t.y, e.y), + if_then_else(c, t.z, e.z), if_then_else(c, t.w, e.w)); +} + +SI ivec4 if_then_else(int32_t c, ivec4 t, ivec4 e) { return c ? t : e; } + +ivec4 operator&(I32 a, ivec4_scalar b) { + return ivec4(a & b.x, a & b.y, a & b.z, a & b.w); +} + +struct bvec3_scalar { + bool x; + bool y; + bool z; + + bvec3_scalar() : bvec3_scalar(false) {} + constexpr bvec3_scalar(bool a) : x(a), y(a), z(a) {} + constexpr bvec3_scalar(bool x, bool y, bool z) : x(x), y(y), z(z) {} +}; + +struct bvec3 { + bvec3() : bvec3(0) {} + bvec3(Bool a) : x(a), y(a), z(a) {} + bvec3(Bool x, Bool y, Bool z) : x(x), y(y), z(z) {} + Bool& select(XYZW c) { + switch (c) { + case X: + return x; + case Y: + return y; + case Z: + return z; + default: + UNREACHABLE; + } + } + Bool sel(XYZW c1) { return select(c1); } + + Bool x; + Bool y; + Bool z; +}; + +struct bvec4_scalar { + bool x; + bool y; + bool z; + bool w; + + bvec4_scalar() : bvec4_scalar(false) {} + constexpr bvec4_scalar(bool a) : x(a), y(a), z(a), w(a) {} + constexpr bvec4_scalar(bool x, bool y, bool z, bool w) + : x(x), y(y), z(z), w(w) {} +}; + +struct bvec4 { + bvec4() : bvec4(0) {} + bvec4(Bool a) : x(a), y(a), z(a), w(a) {} + bvec4(Bool x, Bool y, Bool z, Bool w) : x(x), y(y), z(z), w(w) {} + bvec4(bvec2 x, bvec2 y) : x(x.x), y(x.y), z(y.x), w(y.y) {} + Bool& select(XYZW c) { + switch (c) { + case X: + return x; + case Y: + return y; + case Z: + return z; + case W: + return w; + } + } + Bool sel(XYZW c1) { return select(c1); } + + Bool x; + Bool y; + Bool z; + Bool w; +}; + +bvec4_scalar make_bvec4(bool n) { return bvec4_scalar{n, n, n, n}; } + +bvec4_scalar make_bvec4(bool x, bool y, bool z, bool w) { + return bvec4_scalar{x, y, z, w}; +} + +template +bvec4 make_bvec4(const N& n) { + return bvec4(n); +} + +template +bvec4 make_bvec4(const X& x, const Y& y) { + return bvec4(x, y); +} + +template +bvec4 make_bvec4(const X& x, const Y& y, const Z& z, const W& w) { + return bvec4(x, y, z, w); +} + +struct vec2_ref { + vec2_ref(Float& x, Float& y) : x(x), y(y) {} + Float& x; + Float& y; + + Float& select(XYZW c) { + switch (c) { + case X: + return x; + case Y: + return y; + default: + UNREACHABLE; + } + } + Float& sel(XYZW c1) { return select(c1); } + + vec2_ref& operator=(const vec2& a) { + x = a.x; + y = a.y; + return *this; + } + + vec2_ref& operator/=(Float a) { + x /= a; + y /= a; + return *this; + } + + vec2_ref& operator/=(vec2 a) { + x /= a.x; + y /= a.y; + return *this; + } + + vec2_ref& operator+=(vec2 a) { + x += a.x; + y += a.y; + return *this; + } + vec2_ref& operator-=(vec2 a) { + x -= a.x; + y -= a.y; + return *this; + } + vec2_ref& operator*=(vec2 a) { + x *= a.x; + y *= a.y; + return *this; + } +}; + +struct vec3_scalar { + typedef struct vec3 vector_type; + typedef float element_type; + + float x; + float y; + float z; + + constexpr vec3_scalar() : vec3_scalar(0.0f) {} + constexpr vec3_scalar(float a) : x(a), y(a), z(a) {} + constexpr vec3_scalar(float x, float y, float z) : x(x), y(y), z(z) {} + + float& select(XYZW c) { + switch (c) { + case X: + return x; + case Y: + return y; + case Z: + return z; + default: + UNREACHABLE; + } + } + float& sel(XYZW c1) { return select(c1); } + vec2_scalar sel(XYZW c1, XYZW c2) { + return vec2_scalar(select(c1), select(c2)); + } + vec3_scalar sel(XYZW c1, XYZW c2, XYZW c3) { + return vec3_scalar(select(c1), select(c2), select(c3)); + } + vec2_scalar_ref lsel(XYZW c1, XYZW c2) { + return vec2_scalar_ref(select(c1), select(c2)); + } + + friend vec3_scalar operator*(vec3_scalar a, float b) { + return vec3_scalar{a.x * b, a.y * b, a.z * b}; + } + + friend vec3_scalar operator-(vec3_scalar a, vec3_scalar b) { + return vec3_scalar{a.x - b.x, a.y - b.y, a.z - b.z}; + } + friend vec3_scalar operator+(vec3_scalar a, vec3_scalar b) { + return vec3_scalar{a.x + b.x, a.y + b.y, a.z + b.z}; + } + + friend vec3_scalar operator/(vec3_scalar a, float b) { + return vec3_scalar{a.x / b, a.y / b, a.z / b}; + } + + friend bool operator==(const vec3_scalar& l, const vec3_scalar& r) { + return l.x == r.x && l.y == r.y && l.z == r.z; + } +}; + +struct vec3_scalar_ref { + vec3_scalar_ref(float& x, float& y, float& z) : x(x), y(y), z(z) {} + float& x; + float& y; + float& z; + + float& select(XYZW c) { + switch (c) { + case X: + return x; + case Y: + return y; + case Z: + return z; + default: + UNREACHABLE; + } + } + float& sel(XYZW c1) { return select(c1); } + + vec3_scalar_ref& operator=(const vec3_scalar& a) { + x = a.x; + y = a.y; + z = a.z; + return *this; + } + + operator vec3_scalar() const { return vec3_scalar{x, y, z}; } +}; + +struct vec3 { + typedef struct vec3 vector_type; + typedef float element_type; + + constexpr vec3() : vec3(Float(0.0f)) {} + constexpr vec3(Float a) : x(a), y(a), z(a) {} + constexpr vec3(Float x, Float y, Float z) : x(x), y(y), z(z) {} + vec3(vec2 a, Float z) : x(a.x), y(a.y), z(z) {} + constexpr vec3(vec3_scalar s) : x(s.x), y(s.y), z(s.z) {} + constexpr vec3(vec3_scalar s0, vec3_scalar s1, vec3_scalar s2, vec3_scalar s3) + : x(Float{s0.x, s1.x, s2.x, s3.x}), + y(Float{s0.y, s1.y, s2.y, s3.y}), + z(Float{s0.z, s1.z, s2.z, s3.z}) {} + Float x; + Float y; + Float z; + + Float& select(XYZW c) { + switch (c) { + case X: + return x; + case Y: + return y; + case Z: + return z; + default: + UNREACHABLE; + } + } + Float& sel(XYZW c1) { return select(c1); } + + vec2 sel(XYZW c1, XYZW c2) { return vec2(select(c1), select(c2)); } + + vec3 sel(XYZW c1, XYZW c2, XYZW c3) { + return vec3(select(c1), select(c2), select(c3)); + } + + vec2_ref lsel(XYZW c1, XYZW c2) { return vec2_ref(select(c1), select(c2)); } + + friend vec3 operator*(vec3 a, Float b) { + return vec3(a.x * b, a.y * b, a.z * b); + } + friend vec3 operator*(vec3 a, vec3 b) { + return vec3(a.x * b.x, a.y * b.y, a.z * b.z); + } + friend vec3 operator*(Float a, vec3 b) { + return vec3(a * b.x, a * b.y, a * b.z); + } + + friend vec3 operator/(vec3 a, Float b) { + return vec3(a.x / b, a.y / b, a.z / b); + } + + friend I32 operator==(const vec3& l, const vec3& r) { + return l.x == r.x && l.y == r.y && l.z == r.z; + } + + friend vec3 operator-(vec3 a, Float b) { + return vec3(a.x - b, a.y - b, a.z - b); + } + friend vec3 operator-(vec3 a, vec3 b) { + return vec3(a.x - b.x, a.y - b.y, a.z - b.z); + } + friend vec3 operator+(vec3 a, Float b) { + return vec3(a.x + b, a.y + b, a.z + b); + } + friend vec3 operator+(vec3 a, vec3 b) { + return vec3(a.x + b.x, a.y + b.y, a.z + b.z); + } + + vec3 operator+=(vec3_scalar a) { + x += a.x; + y += a.y; + z += a.z; + return *this; + } +}; + +vec3_scalar force_scalar(const vec3& v) { + return vec3_scalar{force_scalar(v.x), force_scalar(v.y), force_scalar(v.z)}; +} + +vec3_scalar make_vec3(float n) { return vec3_scalar{n, n, n}; } + +vec3_scalar make_vec3(const vec2_scalar& v, float z) { + return vec3_scalar{v.x, v.y, z}; +} + +vec3_scalar make_vec3(float x, float y, float z) { + return vec3_scalar{x, y, z}; +} + +vec3_scalar make_vec3(int32_t x, int32_t y, float z) { + return vec3_scalar{float(x), float(y), z}; +} + +template +vec3 make_vec3(const N& n) { + return vec3(n); +} + +template +vec3 make_vec3(const X& x, const Y& y) { + return vec3(x, y); +} + +template +vec3 make_vec3(const X& x, const Y& y, const Z& z) { + return vec3(x, y, z); +} + +SI vec3 if_then_else(I32 c, vec3 t, vec3 e) { + return vec3(if_then_else(c, t.x, e.x), if_then_else(c, t.y, e.y), + if_then_else(c, t.z, e.z)); +} + +SI vec3 if_then_else(int32_t c, vec3 t, vec3 e) { return c ? t : e; } + +SI vec3 if_then_else(ivec3 c, vec3 t, vec3 e) { + return vec3(if_then_else(c.x, t.x, e.x), if_then_else(c.y, t.y, e.y), + if_then_else(c.z, t.z, e.z)); +} + +vec3 step(vec3 edge, vec3 x) { + return vec3(step(edge.x, x.x), step(edge.y, x.y), step(edge.z, x.z)); +} + +SI vec3 min(vec3 a, vec3 b) { + return vec3(min(a.x, b.x), min(a.y, b.y), min(a.z, b.z)); +} +SI vec3 max(vec3 a, vec3 b) { + return vec3(max(a.x, b.x), max(a.y, b.y), max(a.z, b.z)); +} + +SI vec3_scalar max(vec3_scalar a, vec3_scalar b) { + return vec3_scalar{max(a.x, b.x), max(a.y, b.y), max(a.z, b.z)}; +} + +vec3 pow(vec3 x, vec3 y) { + return vec3(pow(x.x, y.x), pow(x.y, y.y), pow(x.z, y.z)); +} + +struct vec3_ref { + vec3_ref(Float& x, Float& y, Float& z) : x(x), y(y), z(z) {} + Float& x; + Float& y; + Float& z; + vec3_ref& operator=(const vec3& a) { + x = a.x; + y = a.y; + z = a.z; + return *this; + } + + vec3_ref& operator/=(Float a) { + x /= a; + y /= a; + z /= a; + return *this; + } + + vec3_ref& operator*=(Float a) { + x *= a; + y *= a; + z *= a; + return *this; + } +}; + +struct vec4_scalar { + typedef struct vec4 vector_type; + typedef float element_type; + + float x; + float y; + float z; + float w; + + constexpr vec4_scalar() : vec4_scalar(0.0f) {} + constexpr vec4_scalar(float a) : x(a), y(a), z(a), w(a) {} + constexpr vec4_scalar(float x, float y, float z, float w) + : x(x), y(y), z(z), w(w) {} + + float& select(XYZW c) { + switch (c) { + case X: + return x; + case Y: + return y; + case Z: + return z; + case W: + return w; + default: + UNREACHABLE; + } + } + float& sel(XYZW c1) { return select(c1); } + vec2_scalar sel(XYZW c1, XYZW c2) { + return vec2_scalar{select(c1), select(c2)}; + } + vec3_scalar sel(XYZW c1, XYZW c2, XYZW c3) { + return vec3_scalar{select(c1), select(c2), select(c3)}; + } + vec2_scalar_ref lsel(XYZW c1, XYZW c2) { + return vec2_scalar_ref(select(c1), select(c2)); + } + vec3_scalar_ref lsel(XYZW c1, XYZW c2, XYZW c3) { + return vec3_scalar_ref(select(c1), select(c2), select(c3)); + } + + friend vec4_scalar operator*(vec4_scalar a, vec4_scalar b) { + return vec4_scalar{a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w}; + } + friend vec4_scalar operator*(vec4_scalar a, float b) { + return vec4_scalar{a.x * b, a.y * b, a.z * b, a.w * b}; + } + vec4_scalar& operator*=(float a) { + x *= a; + y *= a; + z *= a; + w *= a; + return *this; + } + + friend vec4_scalar operator-(vec4_scalar a, vec4_scalar b) { + return vec4_scalar{a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w}; + } + friend vec4_scalar operator+(vec4_scalar a, vec4_scalar b) { + return vec4_scalar{a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w}; + } + + friend vec4_scalar operator/(vec4_scalar a, vec4_scalar b) { + return vec4_scalar{a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w}; + } + vec4_scalar& operator/=(vec4_scalar a) { + x /= a.x; + y /= a.y; + z /= a.z; + w /= a.w; + return *this; + } +}; + +vec4_scalar vec2_scalar::sel(XYZW c1, XYZW c2, XYZW c3, XYZW c4) { + return vec4_scalar{select(c1), select(c2), select(c3), select(c4)}; +} + +struct vec4 { + typedef struct vec4 vector_type; + typedef float element_type; + + constexpr vec4() : vec4(Float(0.0f)) {} + constexpr vec4(Float a) : x(a), y(a), z(a), w(a) {} + vec4(Float x, Float y, Float z, Float w) : x(x), y(y), z(z), w(w) {} + vec4(vec3 xyz, Float w) : x(xyz.x), y(xyz.y), z(xyz.z), w(w) {} + vec4(vec2 xy, vec2 zw) : x(xy.x), y(xy.y), z(zw.x), w(zw.y) {} + vec4(vec2 xy, Float z, Float w) : x(xy.x), y(xy.y), z(z), w(w) {} + vec4(Float x, Float y, vec2 zw) : x(x), y(y), z(zw.x), w(zw.y) {} + constexpr vec4(vec4_scalar s) : x(s.x), y(s.y), z(s.z), w(s.w) {} + constexpr vec4(vec4_scalar s0, vec4_scalar s1, vec4_scalar s2, vec4_scalar s3) + : x(Float{s0.x, s1.x, s2.x, s3.x}), + y(Float{s0.y, s1.y, s2.y, s3.y}), + z(Float{s0.z, s1.z, s2.z, s3.z}), + w(Float{s0.w, s1.w, s2.w, s3.w}) {} + Float& select(XYZW c) { + switch (c) { + case X: + return x; + case Y: + return y; + case Z: + return z; + case W: + return w; + default: + UNREACHABLE; + } + } + Float& sel(XYZW c1) { return select(c1); } + + vec2 sel(XYZW c1, XYZW c2) { return vec2(select(c1), select(c2)); } + + vec3 sel(XYZW c1, XYZW c2, XYZW c3) { + return vec3(select(c1), select(c2), select(c3)); + } + vec3_ref lsel(XYZW c1, XYZW c2, XYZW c3) { + return vec3_ref(select(c1), select(c2), select(c3)); + } + + vec2_ref lsel(XYZW c1, XYZW c2) { return vec2_ref(select(c1), select(c2)); } + + Float& operator[](int index) { + switch (index) { + case 0: + return x; + case 1: + return y; + case 2: + return z; + case 3: + return w; + default: + UNREACHABLE; + } + } + + // glsl supports non-const indexing of vecs. + // hlsl doesn't. The code it generates is probably not wonderful. + Float operator[](I32 index) { + float sel_x; + switch (index.x) { + case 0: + sel_x = x.x; + break; + case 1: + sel_x = y.x; + break; + case 2: + sel_x = z.x; + break; + case 3: + sel_x = w.x; + break; + } + float sel_y; + switch (index.y) { + case 0: + sel_y = x.y; + break; + case 1: + sel_y = y.y; + break; + case 2: + sel_y = z.y; + break; + case 3: + sel_y = w.y; + break; + } + float sel_z; + switch (index.z) { + case 0: + sel_z = x.z; + break; + case 1: + sel_z = y.z; + break; + case 2: + sel_z = z.z; + break; + case 3: + sel_z = w.z; + break; + } + float sel_w; + switch (index.w) { + case 0: + sel_w = x.w; + break; + case 1: + sel_w = y.w; + break; + case 2: + sel_w = z.w; + break; + case 3: + sel_w = w.w; + break; + } + Float ret = {sel_x, sel_y, sel_z, sel_w}; + return ret; + } + + friend vec4 operator/(vec4 a, Float b) { + return vec4(a.x / b, a.y / b, a.z / b, a.w / b); + } + friend vec4 operator/(vec4 a, vec4 b) { + return vec4(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w); + } + + friend vec4 operator*(vec4 a, Float b) { + return vec4(a.x * b, a.y * b, a.z * b, a.w * b); + } + + friend vec4 operator*(Float b, vec4 a) { + return vec4(a.x * b, a.y * b, a.z * b, a.w * b); + } + friend vec4 operator*(vec4 a, vec4 b) { + return vec4(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w); + } + + friend vec4 operator-(vec4 a, vec4 b) { + return vec4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); + } + friend vec4 operator+(vec4 a, vec4 b) { + return vec4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); + } + vec4& operator+=(vec4 a) { + x += a.x; + y += a.y; + z += a.z; + w += a.w; + return *this; + } + vec4& operator/=(vec4 a) { + x /= a.x; + y /= a.y; + z /= a.z; + w /= a.w; + return *this; + } + vec4& operator*=(Float a) { + x *= a; + y *= a; + z *= a; + w *= a; + return *this; + } + + Float x; + Float y; + Float z; + Float w; +}; + +vec4_scalar force_scalar(const vec4& v) { + return vec4_scalar{force_scalar(v.x), force_scalar(v.y), force_scalar(v.z), + force_scalar(v.w)}; +} + +vec4_scalar make_vec4(float n) { return vec4_scalar{n, n, n, n}; } + +vec4_scalar make_vec4(const vec2_scalar& v, float z, float w) { + return vec4_scalar{v.x, v.y, z, w}; +} + +vec4_scalar make_vec4(const vec2_scalar& a, const vec2_scalar& b) { + return vec4_scalar{a.x, a.y, b.x, b.y}; +} + +vec4_scalar make_vec4(const vec3_scalar& v, float w) { + return vec4_scalar{v.x, v.y, v.z, w}; +} + +vec4_scalar make_vec4(float x, float y, float z, float w) { + return vec4_scalar{x, y, z, w}; +} + +vec4_scalar make_vec4(float x, float y, const vec2_scalar& v) { + return vec4_scalar{x, y, v.x, v.y}; +} + +template +vec4 make_vec4(const N& n) { + return vec4(n); +} + +template +vec4 make_vec4(const X& x, const Y& y) { + return vec4(x, y); +} + +template +vec4 make_vec4(const X& x, const Y& y, const Z& z) { + return vec4(x, y, z); +} + +template +vec4 make_vec4(const X& x, const Y& y, const Z& z, const W& w) { + return vec4(x, y, z, w); +} + +SI ivec4 roundto(vec4 v, Float scale) { + return ivec4(roundto(v.x, scale), roundto(v.y, scale), roundto(v.z, scale), + roundto(v.w, scale)); +} + +vec4 operator*(vec4_scalar a, Float b) { + return vec4(a.x * b, a.y * b, a.z * b, a.w * b); +} + +SI vec4 if_then_else(I32 c, vec4 t, vec4 e) { + return vec4(if_then_else(c, t.x, e.x), if_then_else(c, t.y, e.y), + if_then_else(c, t.z, e.z), if_then_else(c, t.w, e.w)); +} + +SI vec4 if_then_else(int32_t c, vec4 t, vec4 e) { return c ? t : e; } + +SI vec2 clamp(vec2 a, vec2 minVal, vec2 maxVal) { + return vec2(clamp(a.x, minVal.x, maxVal.x), clamp(a.y, minVal.y, maxVal.y)); +} + +SI vec2_scalar clamp(vec2_scalar a, vec2_scalar minVal, vec2_scalar maxVal) { + return vec2_scalar{clamp(a.x, minVal.x, maxVal.x), + clamp(a.y, minVal.y, maxVal.y)}; +} + +SI I32 clamp(I32 a, I32 minVal, I32 maxVal) { + a = if_then_else(a < minVal, minVal, a); + return if_then_else(a > maxVal, maxVal, a); +} + +SI vec3 clamp(vec3 a, vec3 minVal, vec3 maxVal) { + return vec3(clamp(a.x, minVal.x, maxVal.x), clamp(a.y, minVal.y, maxVal.y), + clamp(a.z, minVal.z, maxVal.z)); +} + +SI vec4 clamp(vec4 a, vec4 minVal, vec4 maxVal) { + return vec4(clamp(a.x, minVal.x, maxVal.x), clamp(a.y, minVal.y, maxVal.y), + clamp(a.z, minVal.z, maxVal.z), clamp(a.w, minVal.w, maxVal.w)); +} +template +auto lessThanEqual(T x, T y) -> decltype(x <= y) { + return x <= y; +} + +template +auto lessThan(T x, T y) -> decltype(x < y) { + return x < y; +} + +SI bvec3 lessThanEqual(vec3 x, vec3 y) { + return bvec3(lessThanEqual(x.x, y.x), lessThanEqual(x.y, y.y), + lessThanEqual(x.z, y.z)); +} + +SI bvec2 lessThanEqual(vec2 x, vec2 y) { + return bvec2(lessThanEqual(x.x, y.x), lessThanEqual(x.y, y.y)); +} + +SI bvec2_scalar lessThanEqual(vec2_scalar x, vec2_scalar y) { + return bvec2_scalar{lessThanEqual(x.x, y.x), lessThanEqual(x.y, y.y)}; +} + +SI bvec4 lessThanEqual(vec4 x, vec4 y) { + return bvec4(lessThanEqual(x.x, y.x), lessThanEqual(x.y, y.y), + lessThanEqual(x.z, y.z), lessThanEqual(x.w, y.w)); +} + +SI bvec4_scalar lessThanEqual(vec4_scalar x, vec4_scalar y) { + return bvec4_scalar{lessThanEqual(x.x, y.x), lessThanEqual(x.y, y.y), + lessThanEqual(x.z, y.z), lessThanEqual(x.w, y.w)}; +} + +SI bvec2 lessThan(vec2 x, vec2 y) { + return bvec2(lessThan(x.x, y.x), lessThan(x.y, y.y)); +} + +template +auto greaterThan(T x, T y) -> decltype(x > y) { + return x > y; +} + +bvec2 greaterThan(vec2 x, vec2 y) { + return bvec2(greaterThan(x.x, y.x), greaterThan(x.y, y.y)); +} + +template +auto greaterThanEqual(T x, T y) -> decltype(x >= y) { + return x >= y; +} + +bvec4 greaterThanEqual(vec4 x, vec4 y) { + return bvec4(greaterThanEqual(x.x, y.x), greaterThanEqual(x.y, y.y), + greaterThanEqual(x.z, y.z), greaterThanEqual(x.w, y.w)); +} + +enum TextureFormat { RGBA32F, RGBA32I, RGBA8, R8 }; + +enum TextureFilter { NEAREST, LINEAR }; + +struct samplerCommon { + uint32_t* buf = nullptr; + uint32_t stride = 0; // in dwords + uint32_t height = 0; + uint32_t width = 0; + TextureFormat format = TextureFormat::RGBA8; +}; + +struct samplerDepth { + int depth = 0; + uint32_t height_stride = 0; // in dwords +}; + +struct samplerFilter { + TextureFilter filter = TextureFilter::NEAREST; +}; + +struct sampler2DArray_impl : samplerCommon, samplerDepth, samplerFilter {}; +typedef sampler2DArray_impl* sampler2DArray; + +typedef struct sampler2DArrayR8_impl : sampler2DArray_impl{} * sampler2DArrayR8; +typedef struct sampler2DArrayRGBA8_impl : sampler2DArray_impl{} * + sampler2DArrayRGBA8; +typedef struct sampler2DArrayRGBA32F_impl : sampler2DArray_impl{} * + sampler2DArrayRGBA32F; + +struct sampler2D_impl : samplerCommon, samplerFilter {}; +typedef sampler2D_impl* sampler2D; + +typedef struct sampler2DR8_impl : sampler2D_impl{} * sampler2DR8; +typedef struct sampler2DRGBA8_impl : sampler2D_impl{} * sampler2DRGBA8; +typedef struct sampler2DRGBA32F_impl : sampler2D_impl{} * sampler2DRGBA32F; + +struct isampler2D_impl : samplerCommon {}; +typedef isampler2D_impl* isampler2D; + +struct isampler2DRGBA32I_impl : isampler2D_impl {}; +typedef isampler2DRGBA32I_impl* isampler2DRGBA32I; + +struct mat4_scalar; + +struct mat2_scalar { + vec2_scalar data[2]; + + mat2_scalar() = default; + constexpr mat2_scalar(float a) { + data[0] = vec2_scalar(a); + data[1] = vec2_scalar(a); + } + constexpr mat2_scalar(vec2_scalar a, vec2_scalar b) { + data[0] = a; + data[1] = b; + } + mat2_scalar(const mat4_scalar& mat); + + vec2_scalar& operator[](int index) { return data[index]; } + const vec2_scalar& operator[](int index) const { return data[index]; } + + friend vec2_scalar operator*(mat2_scalar m, vec2_scalar v) { + vec2_scalar u; + u.x = m[0].x * v.x + m[1].x * v.y; + u.y = m[0].y * v.x + m[1].y * v.y; + return u; + } + + friend vec2 operator*(mat2_scalar m, vec2 v) { + vec2 u; + u.x = m[0].x * v.x + m[1].x * v.y; + u.y = m[0].y * v.x + m[1].y * v.y; + return u; + } + + friend mat2_scalar operator*(mat2_scalar m, float f) { + mat2_scalar u = m; + u[0].x *= f; + u[0].y *= f; + u[1].x *= f; + u[1].y *= f; + return u; + } +}; + +struct mat4; + +struct mat2 { + vec2 data[2]; + + vec2& operator[](int index) { return data[index]; } + const vec2& operator[](int index) const { return data[index]; } + mat2() = default; + + mat2(Float a) { + data[0] = vec2(a); + data[1] = vec2(a); + } + + mat2(vec2 a, vec2 b) { + data[0] = a; + data[1] = b; + } + mat2(const mat4& mat); + constexpr mat2(mat2_scalar s) { + data[0] = vec2(s.data[0]); + data[1] = vec2(s.data[1]); + } + + friend vec2 operator*(mat2 m, vec2 v) { + vec2 u; + u.x = m[0].x * v.x + m[1].x * v.y; + u.y = m[0].y * v.x + m[1].y * v.y; + return u; + } + friend mat2 operator*(mat2 m, Float f) { + mat2 u = m; + u[0].x *= f; + u[0].y *= f; + u[1].x *= f; + u[1].y *= f; + return u; + } +}; + +mat2_scalar make_mat2(float n) { return mat2_scalar{{n, n}, {n, n}}; } + +mat2_scalar make_mat2(const mat2_scalar& m) { return m; } + +mat2_scalar make_mat2(const vec2_scalar& x, const vec2_scalar& y) { + return mat2_scalar{x, y}; +} + +template +mat2 make_mat2(const N& n) { + return mat2(n); +} + +template +mat2 make_mat2(const X& x, const Y& y) { + return mat2(x, y); +} + +SI mat2 if_then_else(I32 c, mat2 t, mat2 e) { + return mat2(if_then_else(c, t[0], e[0]), if_then_else(c, t[0], e[1])); +} + +SI mat2 if_then_else(int32_t c, mat2 t, mat2 e) { return c ? t : e; } + +struct mat3_scalar { + vec3_scalar data[3]; + + mat3_scalar() = default; + constexpr mat3_scalar(vec3_scalar a, vec3_scalar b, vec3_scalar c) { + data[0] = a; + data[1] = b; + data[2] = c; + } + mat3_scalar(const mat4_scalar& mat); + + vec3_scalar& operator[](int index) { return data[index]; } + const vec3_scalar& operator[](int index) const { return data[index]; } + + friend vec3_scalar operator*(mat3_scalar m, vec3_scalar v) { + vec3_scalar u; + u.x = m[0].x * v.x + m[1].x * v.y + m[2].x * v.z; + u.y = m[0].y * v.x + m[1].y * v.y + m[2].y * v.z; + u.z = m[0].z * v.x + m[1].z * v.y + m[2].z * v.z; + return u; + } + + friend vec3 operator*(mat3_scalar m, vec3 v) { + vec3 u; + u.x = m[0].x * v.x + m[1].x * v.y + m[2].x * v.z; + u.y = m[0].y * v.x + m[1].y * v.y + m[2].y * v.z; + u.z = m[0].z * v.x + m[1].z * v.y + m[2].z * v.z; + return u; + } +}; + +struct mat3 { + vec3 data[3]; + + vec3& operator[](int index) { return data[index]; } + const vec3& operator[](int index) const { return data[index]; } + mat3() = default; + mat3(vec3 a, vec3 b, vec3 c) { + data[0] = a; + data[1] = b; + data[2] = c; + } + + constexpr mat3(mat3_scalar s) { + data[0] = vec3(s.data[0]); + data[1] = vec3(s.data[1]); + data[2] = vec3(s.data[2]); + } + constexpr mat3(mat3_scalar s0, mat3_scalar s1, mat3_scalar s2, + mat3_scalar s3) { + data[0] = vec3(s0.data[0], s1.data[0], s2.data[0], s3.data[0]); + data[1] = vec3(s0.data[1], s1.data[1], s2.data[1], s3.data[1]); + data[2] = vec3(s0.data[2], s1.data[2], s2.data[2], s3.data[2]); + } + + constexpr mat3(Float d1, Float d2, Float d3, Float d4, Float d5, Float d6, + Float d7, Float d8, Float d9) { + data[0] = vec3(d1, d2, d3); + data[1] = vec3(d4, d5, d6); + data[2] = vec3(d7, d8, d9); + } + + mat3(const mat4& mat); + + friend vec3 operator*(mat3 m, vec3 v) { + vec3 u; + u.x = m[0].x * v.x + m[1].x * v.y + m[2].x * v.z; + u.y = m[0].y * v.x + m[1].y * v.y + m[2].y * v.z; + u.z = m[0].z * v.x + m[1].z * v.y + m[2].z * v.z; + return u; + } +}; + +mat3_scalar force_scalar(const mat3& v) { + return mat3_scalar{force_scalar(v[0]), force_scalar(v[1]), + force_scalar(v[2])}; +} + +mat3_scalar make_mat3(const mat3_scalar& m) { return m; } + +mat3_scalar make_mat3(const vec3_scalar& x, const vec3_scalar& y, + const vec3_scalar& z) { + return mat3_scalar{x, y, z}; +} + +constexpr mat3_scalar make_mat3(float m0, float m1, float m2, float m3, + float m4, float m5, float m6, float m7, + float m8) { + return mat3_scalar{{m0, m1, m2}, {m3, m4, m5}, {m6, m7, m8}}; +} + +template +mat3 make_mat3(const N& n) { + return mat3(n); +} + +template +mat3 make_mat3(const X& x, const Y& y, const Z& z) { + return mat3(x, y, z); +} + +struct mat4_scalar { + vec4_scalar data[4]; + + mat4_scalar() = default; + constexpr mat4_scalar(vec4_scalar a, vec4_scalar b, vec4_scalar c, + vec4_scalar d) { + data[0] = a; + data[1] = b; + data[2] = c; + data[3] = d; + } + + vec4_scalar& operator[](int index) { return data[index]; } + const vec4_scalar& operator[](int index) const { return data[index]; } + + static mat4_scalar load_from_ptr(const float* f) { + mat4_scalar m; + // XXX: hopefully this is in the right order + m.data[0] = vec4_scalar{f[0], f[1], f[2], f[3]}; + m.data[1] = vec4_scalar{f[4], f[5], f[6], f[7]}; + m.data[2] = vec4_scalar{f[8], f[9], f[10], f[11]}; + m.data[3] = vec4_scalar{f[12], f[13], f[14], f[15]}; + return m; + } + + friend vec4_scalar operator*(mat4_scalar m, vec4_scalar v) { + vec4_scalar u; + u.x = m[0].x * v.x + m[1].x * v.y + m[2].x * v.z + m[3].x * v.w; + u.y = m[0].y * v.x + m[1].y * v.y + m[2].y * v.z + m[3].y * v.w; + u.z = m[0].z * v.x + m[1].z * v.y + m[2].z * v.z + m[3].z * v.w; + u.w = m[0].w * v.x + m[1].w * v.y + m[2].w * v.z + m[3].w * v.w; + return u; + } + + friend vec4 operator*(mat4_scalar m, vec4 v) { + vec4 u; + u.x = m[0].x * v.x + m[1].x * v.y + m[2].x * v.z + m[3].x * v.w; + u.y = m[0].y * v.x + m[1].y * v.y + m[2].y * v.z + m[3].y * v.w; + u.z = m[0].z * v.x + m[1].z * v.y + m[2].z * v.z + m[3].z * v.w; + u.w = m[0].w * v.x + m[1].w * v.y + m[2].w * v.z + m[3].w * v.w; + return u; + } +}; + +struct mat4 { + vec4 data[4]; + + mat4() = default; + constexpr mat4(mat4_scalar s) { + data[0] = vec4(s.data[0]); + data[1] = vec4(s.data[1]); + data[2] = vec4(s.data[2]); + data[3] = vec4(s.data[3]); + } + + mat4(vec4 a, vec4 b, vec4 c, vec4 d) { + data[0] = a; + data[1] = b; + data[2] = c; + data[3] = d; + } + + vec4& operator[](int index) { return data[index]; } + const vec4& operator[](int index) const { return data[index]; } + + friend vec4 operator*(mat4 m, vec4 v) { + vec4 u; + u.x = m[0].x * v.x + m[1].x * v.y + m[2].x * v.z + m[3].x * v.w; + u.y = m[0].y * v.x + m[1].y * v.y + m[2].y * v.z + m[3].y * v.w; + u.z = m[0].z * v.x + m[1].z * v.y + m[2].z * v.z + m[3].z * v.w; + u.w = m[0].w * v.x + m[1].w * v.y + m[2].w * v.z + m[3].w * v.w; + return u; + } +}; + +mat3::mat3(const mat4& mat) + : mat3(vec3(mat[0].x, mat[0].y, mat[0].z), + vec3(mat[1].x, mat[1].y, mat[1].z), + vec3(mat[2].x, mat[2].y, mat[2].z)) {} + +mat3_scalar::mat3_scalar(const mat4_scalar& mat) + : mat3_scalar(vec3_scalar(mat[0].x, mat[0].y, mat[0].z), + vec3_scalar(mat[1].x, mat[1].y, mat[1].z), + vec3_scalar(mat[2].x, mat[2].y, mat[2].z)) {} + +mat2::mat2(const mat4& mat) + : mat2(vec2(mat[0].x, mat[0].y), vec2(mat[1].x, mat[1].y)) {} + +mat2_scalar::mat2_scalar(const mat4_scalar& mat) + : mat2_scalar(vec2_scalar(mat[0].x, mat[0].y), + vec2_scalar(mat[1].x, mat[1].y)) {} + +mat2_scalar make_mat2(const mat4_scalar& m) { return mat2_scalar(m); } + +mat3_scalar make_mat3(const mat4_scalar& m) { return mat3_scalar(m); } + +mat4_scalar force_scalar(const mat4& v) { + return mat4_scalar(force_scalar(v[0]), force_scalar(v[1]), force_scalar(v[2]), + force_scalar(v[3])); +} + +mat4_scalar make_mat4(const mat4_scalar& m) { return m; } + +mat4_scalar make_mat4(const vec4_scalar& x, const vec4_scalar& y, + const vec4_scalar& z, const vec4_scalar& w) { + return mat4_scalar{x, y, z, w}; +} + +constexpr mat4_scalar make_mat4(float m0, float m1, float m2, float m3, + float m4, float m5, float m6, float m7, + float m8, float m9, float m10, float m11, + float m12, float m13, float m14, float m15) { + return mat4_scalar{{m0, m1, m2, m3}, + {m4, m5, m6, m7}, + {m8, m9, m10, m11}, + {m12, m13, m14, m15}}; +} + +template +mat4 make_mat4(const N& n) { + return mat4(n); +} + +template +mat4 make_mat4(const X& x, const Y& y, const Z& z, const W& w) { + return mat4(x, y, z, w); +} + +SI mat3 if_then_else(I32 c, mat3 t, mat3 e) { + return mat3{if_then_else(c, t[0], e[0]), if_then_else(c, t[1], e[1]), + if_then_else(c, t[2], e[2])}; +} + +SI mat3 if_then_else(int32_t c, mat3 t, mat3 e) { return c ? t : e; } + +SI mat4 if_then_else(I32 c, mat4 t, mat4 e) { + return mat4{if_then_else(c, t[0], e[0]), if_then_else(c, t[1], e[1]), + if_then_else(c, t[2], e[2]), if_then_else(c, t[3], e[3])}; +} + +SI mat4 if_then_else(int32_t c, mat4 t, mat4 e) { return c ? t : e; } + +SI I32 clampCoord(I32 coord, int limit) { +#if USE_SSE2 + return _mm_min_epi16(_mm_max_epi16(coord, _mm_setzero_si128()), + _mm_set1_epi32(limit - 1)); +#else + return clamp(coord, 0, limit - 1); +#endif +} +SI int clampCoord(int coord, int limit) { + return min(max(coord, 0), limit - 1); +} +template +SI T clamp2D(T P, S sampler) { + return T{clampCoord(P.x, sampler->width), clampCoord(P.y, sampler->height)}; +} +template +SI T clamp2DArray(T P, sampler2DArray sampler) { + return T{clampCoord(P.x, sampler->width), clampCoord(P.y, sampler->height), + clampCoord(P.z, sampler->depth)}; +} + +float to_float(uint32_t x) { return x * (1.f / 255.f); } + +vec4 pixel_to_vec4(uint32_t a, uint32_t b, uint32_t c, uint32_t d) { + U32 pixels = {a, b, c, d}; + return vec4(cast((pixels >> 16) & 0xFF), cast((pixels >> 8) & 0xFF), + cast(pixels & 0xFF), cast(pixels >> 24)) * + (1.0f / 255.0f); +} + +vec4 pixel_float_to_vec4(Float a, Float b, Float c, Float d) { + return vec4(Float{a.x, b.x, c.x, d.x}, Float{a.y, b.y, c.y, d.y}, + Float{a.z, b.z, c.z, d.z}, Float{a.w, b.w, c.w, d.w}); +} + +ivec4 pixel_int_to_ivec4(I32 a, I32 b, I32 c, I32 d) { + return ivec4(I32{a.x, b.x, c.x, d.x}, I32{a.y, b.y, c.y, d.y}, + I32{a.z, b.z, c.z, d.z}, I32{a.w, b.w, c.w, d.w}); +} + +vec4_scalar pixel_to_vec4(uint32_t p) { + U32 i = {(p >> 16) & 0xFF, (p >> 8) & 0xFF, p & 0xFF, p >> 24}; + Float f = cast(i) * (1.0f / 255.0f); + return vec4_scalar(f.x, f.y, f.z, f.w); +} + +template +SI vec4 fetchOffsetsRGBA8(S sampler, I32 offset) { + return pixel_to_vec4(sampler->buf[offset.x], sampler->buf[offset.y], + sampler->buf[offset.z], sampler->buf[offset.w]); +} + +vec4 texelFetchRGBA8(sampler2D sampler, ivec2 P, int lod) { + I32 offset = P.x + P.y * sampler->stride; + return fetchOffsetsRGBA8(sampler, offset); +} + +vec4 texelFetchRGBA8(sampler2DArray sampler, ivec3 P, int lod) { + I32 offset = P.x + P.y * sampler->stride + P.z * sampler->height_stride; + return fetchOffsetsRGBA8(sampler, offset); +} + +template +SI Float fetchOffsetsR8(S sampler, I32 offset) { + U32 i = { + ((uint8_t*)sampler->buf)[offset.x], ((uint8_t*)sampler->buf)[offset.y], + ((uint8_t*)sampler->buf)[offset.z], ((uint8_t*)sampler->buf)[offset.w]}; + return cast(i) * (1.0f / 255.0f); +} + +vec4 texelFetchR8(sampler2D sampler, ivec2 P, int lod) { + I32 offset = P.x + P.y * sampler->stride; + return vec4(fetchOffsetsR8(sampler, offset), 0.0f, 0.0f, 1.0f); +} + +vec4 texelFetchR8(sampler2DArray sampler, ivec3 P, int lod) { + I32 offset = P.x + P.y * sampler->stride + P.z * sampler->height_stride; + return vec4(fetchOffsetsR8(sampler, offset), 0.0f, 0.0f, 1.0f); +} + +template +SI vec4 fetchOffsetsFloat(S sampler, I32 offset) { + return pixel_float_to_vec4( + *(Float*)&sampler->buf[offset.x], *(Float*)&sampler->buf[offset.y], + *(Float*)&sampler->buf[offset.z], *(Float*)&sampler->buf[offset.w]); +} + +vec4 texelFetchFloat(sampler2D sampler, ivec2 P, int lod) { + I32 offset = P.x * 4 + P.y * sampler->stride; + return fetchOffsetsFloat(sampler, offset); +} + +vec4 texelFetchFloat(sampler2DArray sampler, ivec3 P, int lod) { + I32 offset = P.x * 4 + P.y * sampler->stride + P.z * sampler->height_stride; + return fetchOffsetsFloat(sampler, offset); +} + +vec4 texelFetch(sampler2D sampler, ivec2 P, int lod) { + P = clamp2D(P, sampler); + if (sampler->format == TextureFormat::RGBA32F) { + return texelFetchFloat(sampler, P, lod); + } else if (sampler->format == TextureFormat::RGBA8) { + return texelFetchRGBA8(sampler, P, lod); + } else { + assert(sampler->format == TextureFormat::R8); + return texelFetchR8(sampler, P, lod); + } +} + +vec4 texelFetch(sampler2DRGBA32F sampler, ivec2 P, int lod) { + P = clamp2D(P, sampler); + assert(sampler->format == TextureFormat::RGBA32F); + return texelFetchFloat(sampler, P, lod); +} + +vec4 texelFetch(sampler2DRGBA8 sampler, ivec2 P, int lod) { + P = clamp2D(P, sampler); + assert(sampler->format == TextureFormat::RGBA8); + return texelFetchRGBA8(sampler, P, lod); +} + +vec4 texelFetch(sampler2DR8 sampler, ivec2 P, int lod) { + P = clamp2D(P, sampler); + assert(sampler->format == TextureFormat::R8); + return texelFetchR8(sampler, P, lod); +} + +vec4_scalar texelFetch(sampler2D sampler, ivec2_scalar P, int lod) { + P = clamp2D(P, sampler); + if (sampler->format == TextureFormat::RGBA32F) { + return *(vec4_scalar*)&sampler->buf[P.x * 4 + P.y * sampler->stride]; + } else { + assert(sampler->format == TextureFormat::RGBA8); + return pixel_to_vec4(sampler->buf[P.x + P.y * sampler->stride]); + } +} + +vec4_scalar texelFetch(sampler2DRGBA32F sampler, ivec2_scalar P, int lod) { + P = clamp2D(P, sampler); + assert(sampler->format == TextureFormat::RGBA32F); + return *(vec4_scalar*)&sampler->buf[P.x * 4 + P.y * sampler->stride]; +} + +vec4_scalar texelFetch(sampler2DRGBA8 sampler, ivec2_scalar P, int lod) { + P = clamp2D(P, sampler); + assert(sampler->format == TextureFormat::RGBA8); + return pixel_to_vec4(sampler->buf[P.x + P.y * sampler->stride]); +} + +vec4_scalar texelFetch(sampler2DR8 sampler, ivec2_scalar P, int lod) { + P = clamp2D(P, sampler); + assert(sampler->format == TextureFormat::R8); + return vec4_scalar{ + to_float(((uint8_t*)sampler->buf)[P.x + P.y * sampler->stride]), 0.0f, + 0.0f, 0.0f}; +} + +vec4 texelFetch(sampler2DArray sampler, ivec3 P, int lod) { + P = clamp2DArray(P, sampler); + if (sampler->format == TextureFormat::RGBA32F) { + return texelFetchFloat(sampler, P, lod); + } else if (sampler->format == TextureFormat::R8) { + return texelFetchR8(sampler, P, lod); + } else { + assert(sampler->format == TextureFormat::RGBA8); + return texelFetchRGBA8(sampler, P, lod); + } +} + +vec4 texelFetch(sampler2DArrayRGBA32F sampler, ivec3 P, int lod) { + P = clamp2DArray(P, sampler); + assert(sampler->format == TextureFormat::RGBA32F); + return texelFetchFloat(sampler, P, lod); +} + +vec4 texelFetch(sampler2DArrayRGBA8 sampler, ivec3 P, int lod) { + P = clamp2DArray(P, sampler); + assert(sampler->format == TextureFormat::RGBA8); + return texelFetchRGBA8(sampler, P, lod); +} + +vec4 texelFetch(sampler2DArrayR8 sampler, ivec3 P, int lod) { + P = clamp2DArray(P, sampler); + assert(sampler->format == TextureFormat::R8); + return texelFetchR8(sampler, P, lod); +} + +template +SI ivec4 fetchOffsetsInt(S sampler, I32 offset) { + return pixel_int_to_ivec4( + *(I32*)&sampler->buf[offset.x], *(I32*)&sampler->buf[offset.y], + *(I32*)&sampler->buf[offset.z], *(I32*)&sampler->buf[offset.w]); +} + +ivec4 texelFetch(isampler2D sampler, ivec2 P, int lod) { + P = clamp2D(P, sampler); + assert(sampler->format == TextureFormat::RGBA32I); + I32 offset = P.x * 4 + P.y * sampler->stride; + return fetchOffsetsInt(sampler, offset); +} + +ivec4_scalar texelFetch(isampler2D sampler, ivec2_scalar P, int lod) { + P = clamp2D(P, sampler); + assert(sampler->format == TextureFormat::RGBA32I); + return *(ivec4_scalar*)&sampler->buf[P.x * 4 + P.y * sampler->stride]; +} + +SI vec4_scalar* texelFetchPtr(sampler2D sampler, ivec2_scalar P, int min_x, + int max_x, int min_y, int max_y) { + P.x = min(max(P.x, -min_x), int(sampler->width) - 1 - max_x); + P.y = min(max(P.y, -min_y), int(sampler->height) - 1 - max_y); + assert(sampler->format == TextureFormat::RGBA32F); + return (vec4_scalar*)&sampler->buf[P.x * 4 + P.y * sampler->stride]; +} + +SI ivec4_scalar* texelFetchPtr(isampler2D sampler, ivec2_scalar P, int min_x, + int max_x, int min_y, int max_y) { + P.x = min(max(P.x, -min_x), int(sampler->width) - 1 - max_x); + P.y = min(max(P.y, -min_y), int(sampler->height) - 1 - max_y); + assert(sampler->format == TextureFormat::RGBA32I); + return (ivec4_scalar*)&sampler->buf[P.x * 4 + P.y * sampler->stride]; +} + +#define texelFetchOffset(sampler, P, lod, offset) \ + texelFetch(sampler, (P) + (offset), lod) + +template +SI R mix(T x, U y, A a) { + return (y - x) * a + x; +} + +SI Float mix(Float x, Float y, Float a) { return (y - x) * a + x; } + +template +SI T mix(T x, T y, float a) { + return (y - x) * a + x; +} + +template +SI T mix(T x, T y, vec4_scalar a) { + return T{mix(x.x, y.x, a.x), mix(x.y, y.y, a.y), mix(x.z, y.z, a.z), + mix(x.w, y.w, a.w)}; +} + +template +vec4 textureLinearRGBA8(S sampler, vec2 P, I32 zoffset = 0) { + assert(sampler->format == TextureFormat::RGBA8); + +#if USE_SSE2 + P.x *= sampler->width * 256.0f; + P.y *= sampler->height * 256.0f; + P -= 0.5f * 256.0f; + ivec2 i(P); + ivec2 frac = i & (I32)0xFF; + i >>= 8; + + __m128i row0 = _mm_min_epi16(_mm_max_epi16(i.y, _mm_setzero_si128()), + _mm_set1_epi32(sampler->height - 1)); + row0 = _mm_madd_epi16(row0, _mm_set1_epi32(sampler->stride)); + row0 = + _mm_add_epi32(row0, _mm_min_epi16(_mm_max_epi16(i.x, _mm_setzero_si128()), + _mm_set1_epi32(sampler->width - 1))); + row0 = _mm_add_epi32(row0, zoffset); + + if (_mm_movemask_epi8(_mm_cmpeq_epi8(_mm_or_si128(frac.x, frac.y), + _mm_setzero_si128())) == 0xFFFF) { + return fetchOffsetsRGBA8(sampler, row0); + } + + __m128i yinside = _mm_andnot_si128( + _mm_cmplt_epi32(i.y, _mm_setzero_si128()), + _mm_cmplt_epi32(i.y, _mm_set1_epi32(sampler->height - 1))); + __m128i row1 = _mm_and_si128(yinside, _mm_set1_epi32(sampler->stride)); + + __m128i xinside = _mm_andnot_si128( + _mm_cmplt_epi32(i.x, _mm_setzero_si128()), + _mm_cmplt_epi32(i.x, _mm_set1_epi32(sampler->width - 1))); + __m128i fracx = _mm_and_si128(xinside, frac.x); + fracx = _mm_shufflelo_epi16(fracx, _MM_SHUFFLE(2, 2, 0, 0)); + fracx = _mm_shufflehi_epi16(fracx, _MM_SHUFFLE(2, 2, 0, 0)); + fracx = _mm_slli_epi16(fracx, 4); + + __m128i fracy = _mm_or_si128(_mm_slli_epi32(frac.y, 16), + _mm_sub_epi32(_mm_set1_epi32(256), frac.y)); + + // r0,g0,b0,a0,r1,g1,b1,a1 \/ R0,G0,B0,A0,R1,G1,B1,A1 + // r0,R0,g0,G0,b0,B0,a0,A0,r1,R1,g1,G1,b1,B1,a1,A1 + // r0_,R0_,g0_,G0_,b0_,B0_,a0_,A0_ /\ r1_,R1_,g1_,G1_,b1_,B1_,a1_,A1_ + // (r0*(256-fracy) + R0*fracy), ... + __m128 r0, r1, r2, r3; +# define FILTER_LANE(out, idx) \ + { \ + uint32_t* buf = &sampler->buf[_mm_cvtsi128_si32( \ + _mm_shuffle_epi32(row0, _MM_SHUFFLE(idx, idx, idx, idx)))]; \ + __m128i cc = _mm_unpacklo_epi8( \ + _mm_loadl_epi64((__m128i*)buf), \ + _mm_loadl_epi64( \ + (__m128i*)(buf + _mm_extract_epi16(row1, 2 * idx)))); \ + __m128i cc0 = _mm_unpacklo_epi8(cc, _mm_setzero_si128()); \ + __m128i cc1 = _mm_unpackhi_epi8(cc, _mm_setzero_si128()); \ + cc = _mm_add_epi16( \ + cc0, _mm_mulhi_epi16(_mm_slli_epi16(_mm_sub_epi16(cc1, cc0), 4), \ + _mm_shuffle_epi32( \ + fracx, _MM_SHUFFLE(idx, idx, idx, idx)))); \ + out = _mm_cvtepi32_ps(_mm_madd_epi16( \ + cc, _mm_shuffle_epi32(fracy, _MM_SHUFFLE(idx, idx, idx, idx)))); \ + } + FILTER_LANE(r0, 0); + FILTER_LANE(r1, 1); + FILTER_LANE(r2, 2); + FILTER_LANE(r3, 3); +# undef FILTER_LANE + + _MM_TRANSPOSE4_PS(r0, r1, r2, r3); + return vec4(r2, r1, r0, r3) * (1.0f / 0xFF00); +#else + P.x *= sampler->width * 128.0f; + P.y *= sampler->height * 128.0f; + P -= 0.5f * 128.0f; + ivec2 i(P); + ivec2 frac = i & (I32)0x7F; + i >>= 7; + + I32 row0 = clampCoord(i.x, sampler->width) + + clampCoord(i.y, sampler->height) * sampler->stride + zoffset; + I32 row1 = row0 + ((i.y > 0 && i.y < int32_t(sampler->height) - 1) & + I32(sampler->stride)); + I16 fracx = + CONVERT(frac.x & (i.x > 0 && i.x < int32_t(sampler->width) - 1), I16); + I16 fracy = CONVERT(frac.y, I16); + + auto a0 = + CONVERT(unaligned_load >(&sampler->buf[row0.x]), V8); + auto a1 = + CONVERT(unaligned_load >(&sampler->buf[row1.x]), V8); + a0 += ((a1 - a0) * fracy.x) >> 7; + + auto b0 = + CONVERT(unaligned_load >(&sampler->buf[row0.y]), V8); + auto b1 = + CONVERT(unaligned_load >(&sampler->buf[row1.y]), V8); + b0 += ((b1 - b0) * fracy.y) >> 7; + + auto abl = zipLow(a0, b0); + auto abh = zipHigh(a0, b0); + abl += ((abh - abl) * fracx.xyxyxyxy) >> 7; + + auto c0 = + CONVERT(unaligned_load >(&sampler->buf[row0.z]), V8); + auto c1 = + CONVERT(unaligned_load >(&sampler->buf[row1.z]), V8); + c0 += ((c1 - c0) * fracy.z) >> 7; + + auto d0 = + CONVERT(unaligned_load >(&sampler->buf[row0.w]), V8); + auto d1 = + CONVERT(unaligned_load >(&sampler->buf[row1.w]), V8); + d0 += ((d1 - d0) * fracy.w) >> 7; + + auto cdl = zipLow(c0, d0); + auto cdh = zipHigh(c0, d0); + cdl += ((cdh - cdl) * fracx.zwzwzwzw) >> 7; + + auto rg = CONVERT(V8(zip2Low(abl, cdl)), V8); + auto ba = CONVERT(V8(zip2High(abl, cdl)), V8); + + auto r = lowHalf(rg); + auto g = highHalf(rg); + auto b = lowHalf(ba); + auto a = highHalf(ba); + return vec4(b, g, r, a) * (1.0f / 255.0f); +#endif +} + +template +vec4 textureLinearR8(S sampler, vec2 P, I32 zoffset = 0) { + assert(sampler->format == TextureFormat::R8); + +#if USE_SSE2 + P.x *= sampler->width * 256.0f; + P.y *= sampler->height * 256.0f; + P -= 0.5f * 256.0f; + ivec2 i(P); + ivec2 frac = i & (I32)0xFF; + i >>= 8; + + __m128i row0 = _mm_min_epi16(_mm_max_epi16(i.y, _mm_setzero_si128()), + _mm_set1_epi32(sampler->height - 1)); + row0 = _mm_madd_epi16(row0, _mm_set1_epi32(sampler->stride)); + row0 = + _mm_add_epi32(row0, _mm_min_epi16(_mm_max_epi16(i.x, _mm_setzero_si128()), + _mm_set1_epi32(sampler->width - 1))); + row0 = _mm_add_epi32(row0, zoffset); + + __m128i yinside = _mm_andnot_si128( + _mm_cmplt_epi32(i.y, _mm_setzero_si128()), + _mm_cmplt_epi32(i.y, _mm_set1_epi32(sampler->height - 1))); + __m128i row1 = _mm_and_si128(yinside, _mm_set1_epi32(sampler->stride)); + + __m128i xinside = _mm_andnot_si128( + _mm_cmplt_epi32(i.x, _mm_setzero_si128()), + _mm_cmplt_epi32(i.x, _mm_set1_epi32(sampler->width - 1))); + __m128i fracx = _mm_and_si128(xinside, frac.x); + fracx = _mm_or_si128(_mm_slli_epi32(fracx, 16), + _mm_sub_epi32(_mm_set1_epi32(256), fracx)); + + __m128i fracy = _mm_slli_epi16(frac.y, 4); + fracy = _mm_shufflelo_epi16(fracy, _MM_SHUFFLE(2, 2, 0, 0)); + fracy = _mm_shufflehi_epi16(fracy, _MM_SHUFFLE(2, 2, 0, 0)); + + uint8_t* buf = (uint8_t*)sampler->buf; + uint8_t* buf0 = + buf + _mm_cvtsi128_si32(_mm_shuffle_epi32(row0, _MM_SHUFFLE(0, 0, 0, 0))); + uint8_t* buf1 = + buf + _mm_cvtsi128_si32(_mm_shuffle_epi32(row0, _MM_SHUFFLE(1, 1, 1, 1))); + uint8_t* buf2 = + buf + _mm_cvtsi128_si32(_mm_shuffle_epi32(row0, _MM_SHUFFLE(2, 2, 2, 2))); + uint8_t* buf3 = + buf + _mm_cvtsi128_si32(_mm_shuffle_epi32(row0, _MM_SHUFFLE(3, 3, 3, 3))); + __m128i cc0 = _mm_unpacklo_epi8( + _mm_setr_epi16(*(uint16_t*)buf0, *(uint16_t*)buf1, *(uint16_t*)buf2, + *(uint16_t*)buf3, 0, 0, 0, 0), + _mm_setzero_si128()); + __m128i cc1 = _mm_unpacklo_epi8( + _mm_setr_epi16(*(uint16_t*)(buf0 + _mm_extract_epi16(row1, 0)), + *(uint16_t*)(buf1 + _mm_extract_epi16(row1, 2)), + *(uint16_t*)(buf2 + _mm_extract_epi16(row1, 4)), + *(uint16_t*)(buf3 + _mm_extract_epi16(row1, 6)), 0, 0, 0, + 0), + _mm_setzero_si128()); + __m128i cc = _mm_add_epi16( + cc0, _mm_mulhi_epi16(_mm_slli_epi16(_mm_sub_epi16(cc1, cc0), 4), fracy)); + __m128 r = _mm_cvtepi32_ps(_mm_madd_epi16(cc, fracx)); + return vec4((Float)r * (1.0f / 0xFF00), 0.0f, 0.0f, 1.0f); +#else + P.x *= sampler->width * 128.0f; + P.y *= sampler->height * 128.0f; + P -= 0.5f * 128.0f; + ivec2 i(P); + ivec2 frac = i & (I32)0x7F; + i >>= 7; + + I32 row0 = clampCoord(i.x, sampler->width) + + clampCoord(i.y, sampler->height) * sampler->stride + zoffset; + I32 row1 = row0 + ((i.y > 0 && i.y < int32_t(sampler->height) - 1) & + I32(sampler->stride)); + I16 fracx = + CONVERT(frac.x & (i.x > 0 && i.x < int32_t(sampler->width) - 1), I16); + I16 fracy = CONVERT(frac.y, I16); + + uint8_t* buf = (uint8_t*)sampler->buf; + auto a0 = unaligned_load >(&buf[row0.x]); + auto b0 = unaligned_load >(&buf[row0.y]); + auto c0 = unaligned_load >(&buf[row0.z]); + auto d0 = unaligned_load >(&buf[row0.w]); + auto abcd0 = CONVERT(combine(combine(a0, b0), combine(c0, d0)), V8); + + auto a1 = unaligned_load >(&buf[row1.x]); + auto b1 = unaligned_load >(&buf[row1.y]); + auto c1 = unaligned_load >(&buf[row1.z]); + auto d1 = unaligned_load >(&buf[row1.w]); + auto abcd1 = CONVERT(combine(combine(a1, b1), combine(c1, d1)), V8); + + abcd0 += ((abcd1 - abcd0) * fracy.xxyyzzww) >> 7; + + abcd0 = SHUFFLE(abcd0, abcd0, 0, 2, 4, 6, 1, 3, 5, 7); + auto abcdl = lowHalf(abcd0); + auto abcdh = highHalf(abcd0); + abcdl += ((abcdh - abcdl) * fracx) >> 7; + + Float r = CONVERT(U16(abcdl), Float); + return vec4(r * (1.0f / 255.0f), 0.0f, 0.0f, 1.0f); +#endif +} + +template +vec4 textureLinearRGBA32F(S sampler, vec2 P, I32 zoffset = 0) { + assert(sampler->format == TextureFormat::RGBA32F); + P.x *= sampler->width; + P.y *= sampler->height; + P -= 0.5f; + vec2 f = floor(P); + vec2 r = P - f; + ivec2 i(f); + ivec2 c = clamp2D(i, sampler); + r.x = if_then_else(i.x < 0 || i.x > sampler->width - 2, 0.0f, r.x); + I32 offset0 = c.x * 4 + c.y * sampler->stride + zoffset; + I32 offset1 = offset0 + if_then_else(r.y < 0 || r.y > sampler->height - 2, 0, + sampler->stride); + + Float c0 = mix(mix(*(Float*)&sampler->buf[offset0.x], + *(Float*)&sampler->buf[offset0.x + 4], r.x), + mix(*(Float*)&sampler->buf[offset1.x], + *(Float*)&sampler->buf[offset1.x + 4], r.x), + r.y); + Float c1 = mix(mix(*(Float*)&sampler->buf[offset0.y], + *(Float*)&sampler->buf[offset0.y + 4], r.x), + mix(*(Float*)&sampler->buf[offset1.y], + *(Float*)&sampler->buf[offset1.y + 4], r.x), + r.y); + Float c2 = mix(mix(*(Float*)&sampler->buf[offset0.z], + *(Float*)&sampler->buf[offset0.z + 4], r.x), + mix(*(Float*)&sampler->buf[offset1.z], + *(Float*)&sampler->buf[offset1.z + 4], r.x), + r.y); + Float c3 = mix(mix(*(Float*)&sampler->buf[offset0.w], + *(Float*)&sampler->buf[offset0.w + 4], r.x), + mix(*(Float*)&sampler->buf[offset1.w], + *(Float*)&sampler->buf[offset1.w + 4], r.x), + r.y); + return pixel_float_to_vec4(c0, c1, c2, c3); +} + +vec4 texture(sampler2D sampler, vec2 P) { + if (sampler->filter == TextureFilter::LINEAR) { + if (sampler->format == TextureFormat::RGBA32F) { + return textureLinearRGBA32F(sampler, P); + } else if (sampler->format == TextureFormat::RGBA8) { + return textureLinearRGBA8(sampler, P); + } else { + assert(sampler->format == TextureFormat::R8); + return textureLinearR8(sampler, P); + } + } else { + ivec2 coord(roundto(P.x, sampler->width), roundto(P.y, sampler->height)); + return texelFetch(sampler, coord, 0); + } +} + +vec4 texture(sampler2DArray sampler, vec3 P, Float layer) { + assert(0); + return vec4(); +} + +vec4 texture(sampler2DArray sampler, vec3 P) { + if (sampler->filter == TextureFilter::LINEAR) { + I32 zoffset = + clampCoord(roundto(P.z, 1.0f), sampler->depth) * sampler->height_stride; + if (sampler->format == TextureFormat::RGBA32F) { + return textureLinearRGBA32F(sampler, vec2(P.x, P.y), zoffset); + } else if (sampler->format == TextureFormat::RGBA8) { + return textureLinearRGBA8(sampler, vec2(P.x, P.y), zoffset); + } else { + assert(sampler->format == TextureFormat::R8); + return textureLinearR8(sampler, vec2(P.x, P.y), zoffset); + } + } else { + // just do nearest for now + ivec3 coord(roundto(P.x, sampler->width), roundto(P.y, sampler->height), + roundto(P.z, 1.0f)); + return texelFetch(sampler, coord, 0); + } +} + +vec4 textureLod(sampler2DArray sampler, vec3 P, float lod) { + assert(lod == 0.0); + return texture(sampler, P); +} + +ivec3_scalar textureSize(sampler2DArray sampler, int) { + return ivec3_scalar{int32_t(sampler->width), int32_t(sampler->height), + int32_t(sampler->depth)}; +} + +ivec2_scalar textureSize(sampler2D sampler, int) { + 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)); +} + +vec4 vec2::sel(XYZW c1, XYZW c2, XYZW c3, XYZW c4) { + return vec4(select(c1), select(c2), select(c3), select(c4)); +} + +bool any(bool x) { return x; } + +Bool any(bvec4 x) { return x.x | x.y | x.z | x.w; } + +bool any(bvec4_scalar x) { return x.x | x.y | x.z | x.w; } + +Bool any(bvec2 x) { return x.x | x.y; } + +bool any(bvec2_scalar x) { return x.x | x.y; } + +bool all(bool x) { return x; } + +Bool all(bvec2 x) { return x.x & x.y; } + +bool all(bvec2_scalar x) { return x.x & x.y; } + +Bool all(bvec4 x) { return x.x & x.y & x.z & x.w; } + +bool all(bvec4_scalar x) { return x.x & x.y & x.z & x.w; } + +SI vec4 if_then_else(bvec4 c, vec4 t, vec4 e) { + return vec4(if_then_else(c.x, t.x, e.x), if_then_else(c.y, t.y, e.y), + if_then_else(c.z, t.z, e.z), if_then_else(c.w, t.w, e.w)); +} +SI vec3 if_then_else(bvec3 c, vec3 t, vec3 e) { + return vec3(if_then_else(c.x, t.x, e.x), if_then_else(c.y, t.y, e.y), + if_then_else(c.z, t.z, e.z)); +} + +SI vec2 if_then_else(bvec2 c, vec2 t, vec2 e) { + return vec2(if_then_else(c.x, t.x, e.x), if_then_else(c.y, t.y, e.y)); +} + +template +SI R mix(T x, T y, bvec4 a) { + return if_then_else(a, y, x); +} + +template +SI R mix(T x, T y, bvec3 a) { + return if_then_else(a, y, x); +} + +template +SI R mix(T x, T y, bvec2 a) { + return if_then_else(a, y, x); +} + +template +SI T mix(T x, T y, bvec4_scalar a) { + return T{a.x ? y.x : x.x, a.y ? y.y : x.y, a.z ? y.z : x.z, a.w ? y.w : x.w}; +} + +template +SI T mix(T x, T y, bvec3_scalar a) { + return T{a.x ? y.x : x.x, a.y ? y.y : x.y, a.z ? y.z : x.z}; +} + +template +SI T mix(T x, T y, bvec2_scalar a) { + return T{a.x ? y.x : x.x, a.y ? y.y : x.y}; +} + +float dot(vec3_scalar a, vec3_scalar b) { + return a.x * b.x + a.y * b.y + a.z * b.z; +} + +Float dot(vec3 a, vec3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; } + +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)}; } + +float cos(float x) { return cosf(x); } + +Float cos(Float v) { return {cosf(v.x), cosf(v.y), cosf(v.z), cosf(v.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); +} + +bvec4_scalar notEqual(ivec4_scalar a, ivec4_scalar b) { + return bvec4_scalar{a.x != b.x, a.y != b.y, a.z != b.z, a.w != b.w}; +} + +mat3 transpose(mat3 m) { + return mat3(vec3(m[0].x, m[1].x, m[2].x), vec3(m[0].y, m[1].y, m[2].y), + vec3(m[0].z, m[1].z, m[2].z)); +} + +mat3_scalar transpose(mat3_scalar m) { + return mat3_scalar{vec3_scalar(m[0].x, m[1].x, m[2].x), + vec3_scalar(m[0].y, m[1].y, m[2].y), + vec3_scalar(m[0].z, m[1].z, m[2].z)}; +} + +vec2 abs(vec2 v) { return vec2(abs(v.x), abs(v.y)); } + +vec2_scalar abs(vec2_scalar v) { return vec2_scalar{fabsf(v.x), fabsf(v.y)}; } + +Float mod(Float a, Float b) { return a - b * floor(a / b); } + +vec2 mod(vec2 a, vec2 b) { return vec2(mod(a.x, b.x), mod(a.y, b.y)); } + +vec3 abs(vec3 v) { return vec3(abs(v.x), abs(v.y), abs(v.z)); } + +mat2 inverse(mat2 v) { + Float det = v[0].x * v[1].y - v[0].y * v[1].x; + return mat2(vec2(v[1].y, -v[0].y), vec2(-v[1].x, v[0].x)) * (1. / det); +} + +mat2_scalar inverse(mat2_scalar v) { + float det = v[0].x * v[1].y - v[0].y * v[1].x; + return mat2_scalar{{v[1].y, -v[0].y}, {-v[1].x, v[0].x}} * (1. / det); +} + +int32_t get_nth(I32 a, int n) { return a[n]; } + +float get_nth(Float a, int n) { return a[n]; } + +float get_nth(float a, int n) { return a; } + +ivec2_scalar get_nth(ivec2 a, int n) { return ivec2_scalar{a.x[n], a.y[n]}; } + +vec2_scalar get_nth(vec2 a, int n) { return vec2_scalar{a.x[n], a.y[n]}; } + +vec3_scalar get_nth(vec3 a, int n) { + return vec3_scalar{a.x[n], a.y[n], a.z[n]}; +} + +vec4_scalar get_nth(vec4 a, int n) { + return vec4_scalar{a.x[n], a.y[n], a.z[n], a.w[n]}; +} + +ivec4_scalar get_nth(ivec4 a, int n) { + return ivec4_scalar{a.x[n], a.y[n], a.z[n], a.w[n]}; +} + +mat3_scalar get_nth(mat3 a, int n) { + return make_mat3(get_nth(a[0], n), get_nth(a[1], n), get_nth(a[2], n)); +} + +void put_nth(Float& dst, int n, float src) { dst[n] = src; } + +void put_nth(I32& dst, int n, int32_t src) { dst[n] = src; } + +void put_nth(ivec2& dst, int n, ivec2_scalar src) { + dst.x[n] = src.x; + dst.y[n] = src.y; +} + +void put_nth(vec2& dst, int n, vec2_scalar src) { + dst.x[n] = src.x; + dst.y[n] = src.y; +} + +void put_nth(vec3& dst, int n, vec3_scalar src) { + dst.x[n] = src.x; + dst.y[n] = src.y; + dst.z[n] = src.z; +} + +void put_nth(ivec4& dst, int n, ivec4_scalar src) { + dst.x[n] = src.x; + dst.y[n] = src.y; + dst.z[n] = src.z; + dst.w[n] = src.w; +} + +void put_nth(vec4& dst, int n, vec4_scalar src) { + dst.x[n] = src.x; + dst.y[n] = src.y; + dst.z[n] = src.z; + dst.w[n] = src.w; +} + +// Use an ElementType type constructor +// so that we can implement element_type for +// Int and Float +template +struct ElementType { + typedef typename V::element_type ty; +}; + +template <> +struct ElementType { + typedef float ty; +}; + +template <> +struct ElementType { + typedef float ty; +}; + +template <> +struct ElementType { + typedef float ty; +}; + +template <> +struct ElementType { + typedef int32_t ty; +}; + +void put_nth_component(ivec2_scalar& dst, int n, int32_t src) { + switch (n) { + case 0: + dst.x = src; + break; + case 1: + dst.y = src; + break; + } +} + +void put_nth_component(ivec4_scalar& dst, int n, int32_t src) { + switch (n) { + case 0: + dst.x = src; + break; + case 1: + dst.y = src; + break; + case 2: + dst.z = src; + break; + case 3: + dst.w = src; + break; + } +} + +void put_nth_component(int& dst, int n, int src) { + switch (n) { + case 0: + dst = src; + break; + } +} + +void put_nth_component(float& dst, int n, float src) { + switch (n) { + case 0: + dst = src; + break; + } +} + +void put_nth_component(vec2_scalar& dst, int n, float src) { + switch (n) { + case 0: + dst.x = src; + break; + case 1: + dst.y = src; + break; + } +} + +void put_nth_component(vec3_scalar& dst, int n, float src) { + switch (n) { + case 0: + dst.x = src; + break; + case 1: + dst.y = src; + break; + case 2: + dst.z = src; + break; + } +} + +void put_nth_component(vec4_scalar& dst, int n, float src) { + switch (n) { + case 0: + dst.x = src; + break; + case 1: + dst.y = src; + break; + case 2: + dst.z = src; + break; + case 3: + dst.w = src; + break; + } +} + +Float init_interp(float init0, float step) { + float init1 = init0 + step; + float init2 = init1 + step; + float init3 = init2 + step; + return {init0, init1, init2, init3}; +} + +vec2 init_interp(vec2_scalar init, vec2_scalar step) { + return vec2(init_interp(init.x, step.x), init_interp(init.y, step.y)); +} + +vec3 init_interp(vec3_scalar init, vec3_scalar step) { + return vec3(init_interp(init.x, step.x), init_interp(init.y, step.y), + init_interp(init.z, step.z)); +} + +vec4 init_interp(vec4_scalar init, vec4_scalar step) { + return vec4(init_interp(init.x, step.x), init_interp(init.y, step.y), + init_interp(init.z, step.z), init_interp(init.w, step.w)); +} + +template +struct Array { + T elements[N]; + T& operator[](size_t i) { return elements[i]; } + const T& operator[](size_t i) const { return elements[i]; } + template + void convert(const Array& s) { + for (size_t i = 0; i < N; ++i) elements[i] = T(s[i]); + } +}; + +template +Array if_then_else(I32 c, Array t, + Array e) { + Array r; + for (size_t i = 0; i < SIZE; i++) { + r[i] = if_then_else(c, t[i], e[i]); + } + return r; +} + +#if USE_SSE2 +bool test_all(Bool cond) { return _mm_movemask_ps(cond) == 0xF; } +bool test_any(Bool cond) { return _mm_movemask_ps(cond) != 0; } +bool test_none(Bool cond) { return _mm_movemask_ps(cond) == 0; } +#else +bool test_all(Bool cond) { + return bit_cast(CONVERT(cond, U8)) == 0xFFFFFFFFU; +} +bool test_any(Bool cond) { return bit_cast(CONVERT(cond, U8)) != 0; } +bool test_none(Bool cond) { return bit_cast(CONVERT(cond, U8)) == 0; } +#endif + +// See lp_build_sample_soa_code( +// lp_build_sample_aos used for common cases +// lp_build_sample_image_linear for an actual mip +// lp_build_sample_fetch_image_linear +// lp_build_lerp_simple + +// sampleQuad2D - does the bilinear lerp on 8bit values expanded to 16bit +// it does the lerp on 4 pixels at a time +// i.e. 4 Vector4s is 4*4*4 shorts +} // namespace glsl diff --git a/swgl/src/lib.rs b/swgl/src/lib.rs new file mode 100644 index 0000000000..e8fc030e0c --- /dev/null +++ b/swgl/src/lib.rs @@ -0,0 +1,12 @@ +/* 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/. */ + +#![crate_name = "swgl"] +#![crate_type = "lib"] + +extern crate gleam; + +mod swgl_fns; + +pub use crate::swgl_fns::*; diff --git a/swgl/src/program.h b/swgl/src/program.h new file mode 100644 index 0000000000..120a5a2bb5 --- /dev/null +++ b/swgl/src/program.h @@ -0,0 +1,142 @@ +/* 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/. */ + +struct VertexAttrib; + +namespace glsl { + +struct VertexShaderImpl; +struct FragmentShaderImpl; + +struct ProgramImpl { + virtual ~ProgramImpl() {} + virtual int get_uniform(const char* name) const = 0; + virtual bool set_sampler(int index, int value) = 0; + virtual void bind_attrib(const char* name, int index) = 0; + virtual int get_attrib(const char* name) const = 0; + virtual VertexShaderImpl* get_vertex_shader() = 0; + virtual FragmentShaderImpl* get_fragment_shader() = 0; +}; + +typedef ProgramImpl* (*ProgramLoader)(); + +struct ShaderImpl { + typedef void (*SetUniform1iFunc)(ShaderImpl*, int index, int value); + typedef void (*SetUniform4fvFunc)(ShaderImpl*, int index, const float* value); + typedef void (*SetUniformMatrix4fvFunc)(ShaderImpl*, int index, + const float* value); + + SetUniform1iFunc set_uniform_1i_func = nullptr; + SetUniform4fvFunc set_uniform_4fv_func = nullptr; + SetUniformMatrix4fvFunc set_uniform_matrix4fv_func = nullptr; + + void set_uniform_1i(int index, int value) { + (*set_uniform_1i_func)(this, index, value); + } + + void set_uniform_4fv(int index, const float* value) { + (*set_uniform_4fv_func)(this, index, value); + } + + void set_uniform_matrix4fv(int index, const float* value) { + (*set_uniform_matrix4fv_func)(this, index, value); + } +}; + +struct VertexShaderImpl : ShaderImpl { + typedef void (*InitBatchFunc)(VertexShaderImpl*, ProgramImpl* prog); + typedef void (*LoadAttribsFunc)(VertexShaderImpl*, ProgramImpl* prog, + VertexAttrib* attribs, + unsigned short* indices, int start, + int instance, int count); + typedef void (*RunFunc)(VertexShaderImpl*, char* flats, char* interps, + size_t interp_stride); + + InitBatchFunc init_batch_func = nullptr; + LoadAttribsFunc load_attribs_func = nullptr; + RunFunc run_func = nullptr; + + vec4 gl_Position; + + void init_batch(ProgramImpl* prog) { (*init_batch_func)(this, prog); } + + ALWAYS_INLINE void load_attribs(ProgramImpl* prog, VertexAttrib* attribs, + unsigned short* indices, int start, + int instance, int count) { + (*load_attribs_func)(this, prog, attribs, indices, start, instance, count); + } + + ALWAYS_INLINE void run(char* flats, char* interps, size_t interp_stride) { + (*run_func)(this, flats, interps, interp_stride); + } +}; + +struct FragmentShaderImpl : ShaderImpl { + typedef void (*InitBatchFunc)(FragmentShaderImpl*, ProgramImpl* prog); + typedef void (*InitPrimitiveFunc)(FragmentShaderImpl*, const void* flats); + typedef void (*InitSpanFunc)(FragmentShaderImpl*, const void* interps, + const void* step, float step_width); + typedef void (*RunFunc)(FragmentShaderImpl*); + typedef void (*SkipFunc)(FragmentShaderImpl*, int chunks); + typedef bool (*UseDiscardFunc)(FragmentShaderImpl*); + typedef void (*DrawSpanRGBA8Func)(FragmentShaderImpl*, uint32_t* buf, + int len); + typedef void (*DrawSpanR8Func)(FragmentShaderImpl*, uint8_t* buf, int len); + + InitBatchFunc init_batch_func = nullptr; + InitPrimitiveFunc init_primitive_func = nullptr; + InitSpanFunc init_span_func = nullptr; + RunFunc run_func = nullptr; + SkipFunc skip_func = nullptr; + UseDiscardFunc use_discard_func = nullptr; + DrawSpanRGBA8Func draw_span_RGBA8_func = nullptr; + DrawSpanR8Func draw_span_R8_func = nullptr; + + vec2 gl_FragCoordXY; + vec2_scalar gl_FragCoordZW; + Bool isPixelDiscarded; + vec4 gl_FragColor; + vec4 gl_SecondaryFragColor; + + ALWAYS_INLINE void step_fragcoord() { gl_FragCoordXY.x += 4; } + + ALWAYS_INLINE void step_fragcoord(int chunks) { + gl_FragCoordXY.x += 4 * chunks; + } + + void init_batch(ProgramImpl* prog) { (*init_batch_func)(this, prog); } + + void init_primitive(const void* flats) { + (*init_primitive_func)(this, flats); + } + + ALWAYS_INLINE void init_span(const void* interps, const void* step, + float step_width) { + (*init_span_func)(this, interps, step, step_width); + } + + ALWAYS_INLINE void run() { (*run_func)(this); } + + ALWAYS_INLINE void skip(int chunks = 1) { (*skip_func)(this, chunks); } + + ALWAYS_INLINE bool use_discard() { return (*use_discard_func)(this); } + + ALWAYS_INLINE void draw_span(uint32_t* buf, int len) { + (*draw_span_RGBA8_func)(this, buf, len); + } + + ALWAYS_INLINE bool has_draw_span(uint32_t*) { + return draw_span_RGBA8_func != nullptr; + } + + ALWAYS_INLINE void draw_span(uint8_t* buf, int len) { + (*draw_span_R8_func)(this, buf, len); + } + + ALWAYS_INLINE bool has_draw_span(uint8_t*) { + return draw_span_R8_func != nullptr; + } +}; + +} // namespace glsl diff --git a/swgl/src/swgl_fns.rs b/swgl/src/swgl_fns.rs new file mode 100644 index 0000000000..0d5dd80faa --- /dev/null +++ b/swgl/src/swgl_fns.rs @@ -0,0 +1,2217 @@ +/* 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/. */ +#![allow(unused_variables)] + +use gleam::gl::*; +use std::ffi::{CStr, CString}; +use std::os::raw::{c_char, c_int, c_void}; +use std::ptr; +use std::str; + +#[allow(unused)] +macro_rules! debug { + ($($x:tt)*) => {}; +} + +extern "C" {} + +extern "C" { + fn ActiveTexture(texture: GLenum); + fn BindTexture(target: GLenum, texture: GLuint); + fn BindBuffer(target: GLenum, buffer: GLuint); + fn BindVertexArray(vao: GLuint); + fn BindFramebuffer(target: GLenum, fb: GLuint); + fn BindRenderbuffer(target: GLenum, rb: GLuint); + fn BlendFunc(srgb: GLenum, drgb: GLenum, sa: GLenum, da: GLenum); + fn BlendColor(r: GLfloat, g: GLfloat, b: GLfloat, a: GLfloat); + fn BlendEquation(mode: GLenum); + fn Enable(cap: GLenum); + fn Disable(cap: GLenum); + fn GenQueries(n: GLsizei, result: *mut GLuint); + fn BeginQuery(target: GLenum, id: GLuint); + fn EndQuery(target: GLenum); + fn GetQueryObjectui64v(id: GLuint, pname: GLenum, params: *mut GLuint64); + fn GenBuffers(n: i32, result: *mut GLuint); + fn GenTextures(n: i32, result: *mut GLuint); + fn GenFramebuffers(n: i32, result: *mut GLuint); + fn GenRenderbuffers(n: i32, result: *mut GLuint); + fn BufferData(target: GLenum, size: GLsizeiptr, data: *const GLvoid, usage: GLenum); + fn BufferSubData(target: GLenum, offset: GLintptr, size: GLsizeiptr, data: *const GLvoid); + fn MapBuffer(target: GLenum, access: GLbitfield) -> *mut c_void; + fn MapBufferRange( + target: GLenum, + offset: GLintptr, + length: GLsizeiptr, + access: GLbitfield, + ) -> *mut c_void; + fn UnmapBuffer(target: GLenum) -> GLboolean; + fn TexStorage2D( + target: GLenum, + levels: GLint, + internal_format: GLenum, + width: GLsizei, + height: GLsizei, + ); + fn FramebufferTexture2D( + target: GLenum, + attachment: GLenum, + textarget: GLenum, + texture: GLuint, + level: GLint, + ); + fn CheckFramebufferStatus(target: GLenum) -> GLenum; + fn InvalidateFramebuffer( + target: GLenum, + num_attachments: GLsizei, + attachments: *const GLenum, + ); + fn TexStorage3D( + target: GLenum, + levels: GLint, + internal_format: GLenum, + width: GLsizei, + height: GLsizei, + depth: GLsizei, + ); + fn TexImage2D( + target: GLenum, + level: GLint, + internal_format: GLint, + width: GLsizei, + height: GLsizei, + border: GLint, + format: GLenum, + ty: GLenum, + data: *const c_void, + ); + fn TexImage3D( + target: GLenum, + level: GLint, + internal_format: GLint, + width: GLsizei, + height: GLsizei, + depth: GLsizei, + border: GLint, + format: GLenum, + ty: GLenum, + data: *const c_void, + ); + fn TexSubImage2D( + target: GLenum, + level: GLint, + xoffset: GLint, + yoffset: GLint, + width: GLsizei, + height: GLsizei, + format: GLenum, + ty: GLenum, + data: *const c_void, + ); + fn TexSubImage3D( + target: GLenum, + level: GLint, + xoffset: GLint, + yoffset: GLint, + zoffset: GLint, + width: GLsizei, + height: GLsizei, + depth: GLsizei, + format: GLenum, + ty: GLenum, + data: *const c_void, + ); + fn GetUniformLocation(program: GLuint, name: *const GLchar) -> GLint; + fn BindAttribLocation(program: GLuint, index: GLuint, name: *const GLchar); + fn GetAttribLocation(program: GLuint, name: *const GLchar) -> GLint; + fn GenVertexArrays(n: i32, result: *mut GLuint); + fn VertexAttribPointer( + index: GLuint, + size: GLint, + type_: GLenum, + normalized: GLboolean, + stride: GLsizei, + offset: *const GLvoid, + ); + fn VertexAttribIPointer( + index: GLuint, + size: GLint, + type_: GLenum, + stride: GLsizei, + offset: *const GLvoid, + ); + fn CreateShader(shader_type: GLenum) -> GLuint; + fn AttachShader(program: GLuint, shader: GLuint); + fn CreateProgram() -> GLuint; + fn Uniform1i(location: GLint, v0: GLint); + fn Uniform4fv(location: GLint, count: GLsizei, value: *const GLfloat); + fn UniformMatrix4fv( + location: GLint, + count: GLsizei, + transpose: GLboolean, + value: *const GLfloat, + ); + + fn DrawElementsInstanced( + mode: GLenum, + count: GLsizei, + type_: GLenum, + indices: *const c_void, + instancecount: GLsizei, + ); + fn EnableVertexAttribArray(index: GLuint); + fn VertexAttribDivisor(index: GLuint, divisor: GLuint); + fn LinkProgram(program: GLuint); + fn UseProgram(program: GLuint); + fn SetViewport(x: GLint, y: GLint, width: GLsizei, height: GLsizei); + fn FramebufferTextureLayer( + target: GLenum, + attachment: GLenum, + texture: GLuint, + level: GLint, + layer: GLint, + ); + fn FramebufferRenderbuffer( + target: GLenum, + attachment: GLenum, + renderbuffertarget: GLenum, + renderbuffer: GLuint, + ); + fn RenderbufferStorage(target: GLenum, internalformat: GLenum, width: GLsizei, height: GLsizei); + fn DepthMask(flag: GLboolean); + fn DepthFunc(func: GLenum); + fn SetScissor(x: GLint, y: GLint, width: GLsizei, height: GLsizei); + fn ClearColor(r: GLfloat, g: GLfloat, b: GLfloat, a: GLfloat); + fn ClearDepth(depth: GLdouble); + fn Clear(mask: GLbitfield); + fn PixelStorei(name: GLenum, param: GLint); + fn ReadPixels( + x: GLint, + y: GLint, + width: GLsizei, + height: GLsizei, + format: GLenum, + ty: GLenum, + data: *mut c_void, + ); + fn Finish(); + fn ShaderSourceByName(shader: GLuint, name: *const GLchar); + fn TexParameteri(target: GLenum, pname: GLenum, param: GLint); + fn CopyImageSubData( + src_name: GLuint, + src_target: GLenum, + src_level: GLint, + src_x: GLint, + src_y: GLint, + src_z: GLint, + dst_name: GLuint, + dst_target: GLenum, + dst_level: GLint, + dst_x: GLint, + dst_y: GLint, + dst_z: GLint, + src_width: GLsizei, + src_height: GLsizei, + src_depth: GLsizei, + ); + fn CopyTexSubImage2D( + target: GLenum, + level: GLint, + xoffset: GLint, + yoffset: GLint, + x: GLint, + y: GLint, + width: GLsizei, + height: GLsizei, + ); + fn CopyTexSubImage3D( + target: GLenum, + level: GLint, + xoffset: GLint, + yoffset: GLint, + zoffset: GLint, + x: GLint, + y: GLint, + width: GLsizei, + height: GLsizei, + ); + fn BlitFramebuffer( + src_x0: GLint, + src_y0: GLint, + src_x1: GLint, + src_y1: GLint, + dst_x0: GLint, + dst_y0: GLint, + dst_x1: GLint, + dst_y1: GLint, + mask: GLbitfield, + filter: GLenum, + ); + fn GetIntegerv(pname: GLenum, params: *mut GLint); + fn GetBooleanv(pname: GLenum, params: *mut GLboolean); + fn GetString(name: GLenum) -> *const c_char; + fn GetStringi(name: GLenum, index: GLuint) -> *const c_char; + fn GetError() -> GLenum; + fn InitDefaultFramebuffer(width: i32, height: i32); + fn GetColorBuffer( + fbo: GLuint, + flush: GLboolean, + width: *mut i32, + height: *mut i32, + ) -> *mut c_void; + fn SetTextureBuffer( + tex: GLuint, + internal_format: GLenum, + width: GLsizei, + height: GLsizei, + buf: *mut c_void, + min_width: GLsizei, + min_height: GLsizei, + ); + fn DeleteTexture(n: GLuint); + fn DeleteRenderbuffer(n: GLuint); + fn DeleteFramebuffer(n: GLuint); + fn DeleteBuffer(n: GLuint); + fn DeleteVertexArray(n: GLuint); + fn DeleteQuery(n: GLuint); + fn DeleteShader(shader: GLuint); + fn DeleteProgram(program: GLuint); + fn Composite( + src_id: GLuint, + src_x: GLint, + src_y: GLint, + src_width: GLsizei, + src_height: GLsizei, + dst_x: GLint, + dst_y: GLint, + opaque: GLboolean, + flip: GLboolean, + ); + fn CreateContext() -> *mut c_void; + fn DestroyContext(ctx: *mut c_void); + fn MakeCurrent(ctx: *mut c_void); +} + +#[derive(Clone)] +pub struct Context(*mut c_void); + +impl Context { + pub fn create() -> Self { + Context(unsafe { CreateContext() }) + } + + pub fn destroy(&self) { + unsafe { + DestroyContext(self.0); + } + } + + pub fn make_current(&self) { + unsafe { + MakeCurrent(self.0); + } + } + + pub fn init_default_framebuffer(&self, width: i32, height: i32) { + unsafe { + InitDefaultFramebuffer(width, height); + } + } + + pub fn get_color_buffer(&self, fbo: GLuint, flush: bool) -> (*mut c_void, i32, i32) { + unsafe { + let mut width: i32 = 0; + let mut height: i32 = 0; + let data_ptr = GetColorBuffer(fbo, flush as GLboolean, &mut width, &mut height); + (data_ptr, width, height) + } + } + + pub fn set_texture_buffer( + &self, + tex: GLuint, + internal_format: GLenum, + width: GLsizei, + height: GLsizei, + buf: *mut c_void, + min_width: GLsizei, + min_height: GLsizei, + ) { + unsafe { + SetTextureBuffer( + tex, + internal_format, + width, + height, + buf, + min_width, + min_height, + ); + } + } + + pub fn composite( + &self, + src_id: GLuint, + src_x: GLint, + src_y: GLint, + src_width: GLsizei, + src_height: GLint, + dst_x: GLint, + dst_y: GLint, + opaque: bool, + flip: bool, + ) { + unsafe { + Composite( + src_id, + src_x, + src_y, + src_width, + src_height, + dst_x, + dst_y, + opaque as GLboolean, + flip as GLboolean, + ); + } + } +} + +impl From<*mut c_void> for Context { + fn from(ptr: *mut c_void) -> Self { + Context(ptr) + } +} + +impl From for *mut c_void { + fn from(ctx: Context) -> Self { + ctx.0 + } +} + +fn calculate_length(width: GLsizei, height: GLsizei, format: GLenum, pixel_type: GLenum) -> usize { + let colors = match format { + RED => 1, + RGB => 3, + BGR => 3, + + RGBA => 4, + BGRA => 4, + + ALPHA => 1, + R16 => 1, + LUMINANCE => 1, + DEPTH_COMPONENT => 1, + _ => panic!("unsupported format for read_pixels: {:?}", format), + }; + let depth = match pixel_type { + UNSIGNED_BYTE => 1, + UNSIGNED_SHORT => 2, + SHORT => 2, + FLOAT => 4, + _ => panic!("unsupported pixel_type for read_pixels: {:?}", pixel_type), + }; + + return (width * height * colors * depth) as usize; +} + +impl Gl for Context { + fn get_type(&self) -> GlType { + GlType::Gl + } + + fn buffer_data_untyped( + &self, + target: GLenum, + size: GLsizeiptr, + data: *const GLvoid, + usage: GLenum, + ) { + debug!( + "buffer_data_untyped {} {} {:?} {}", + target, size, data, usage + ); + //panic!(); + unsafe { + BufferData(target, size, data, usage); + } + } + + fn buffer_sub_data_untyped( + &self, + target: GLenum, + offset: isize, + size: GLsizeiptr, + data: *const GLvoid, + ) { + debug!( + "buffer_sub_data_untyped {} {} {} {:?}", + target, offset, size, data + ); + //panic!(); + unsafe { + BufferSubData(target, offset, size, data); + } + } + + fn map_buffer(&self, target: GLenum, access: GLbitfield) -> *mut c_void { + unsafe { MapBuffer(target, access) } + } + + fn map_buffer_range( + &self, + target: GLenum, + offset: GLintptr, + length: GLsizeiptr, + access: GLbitfield, + ) -> *mut c_void { + unsafe { MapBufferRange(target, offset, length, access) } + } + + fn unmap_buffer(&self, target: GLenum) -> GLboolean { + unsafe { UnmapBuffer(target) } + } + + fn shader_source(&self, shader: GLuint, strings: &[&[u8]]) { + //panic!(); + debug!("shader_source {}", shader); + //for s in strings { + // debug!("{}", str::from_utf8(s).unwrap()); + //} + //panic!(); + for s in strings { + let u = str::from_utf8(s).unwrap(); + const PREFIX: &'static str = "// shader: "; + if let Some(start) = u.find(PREFIX) { + if let Some(end) = u[start ..].find('\n') { + debug!( + "shader name: {}", + u[start + PREFIX.len() .. start + end].trim() + ); + unsafe { + let c_string = CString::new( + u[start + PREFIX.len() .. start + end] + .trim() + .replace(" ", ""), + ) + .unwrap(); + ShaderSourceByName(shader, c_string.as_ptr()); + return; + } + } + } + } + panic!("unknown shader"); + } + + fn tex_buffer(&self, target: GLenum, internal_format: GLenum, buffer: GLuint) { + panic!(); + } + + fn read_buffer(&self, mode: GLenum) { + panic!(); + } + + fn read_pixels_into_buffer( + &self, + x: GLint, + y: GLint, + width: GLsizei, + height: GLsizei, + format: GLenum, + pixel_type: GLenum, + dst_buffer: &mut [u8], + ) { + // Assumes that the user properly allocated the size for dst_buffer. + assert!(calculate_length(width, height, format, pixel_type) == dst_buffer.len()); + + unsafe { + ReadPixels( + x, + y, + width, + height, + format, + pixel_type, + dst_buffer.as_mut_ptr() as *mut c_void, + ); + } + } + + fn read_pixels( + &self, + x: GLint, + y: GLint, + width: GLsizei, + height: GLsizei, + format: GLenum, + pixel_type: GLenum, + ) -> Vec { + let len = calculate_length(width, height, format, pixel_type); + let mut pixels: Vec = Vec::new(); + pixels.reserve(len); + unsafe { + pixels.set_len(len); + } + + self.read_pixels_into_buffer( + x, + y, + width, + height, + format, + pixel_type, + pixels.as_mut_slice(), + ); + + pixels + } + + unsafe fn read_pixels_into_pbo( + &self, + x: GLint, + y: GLint, + width: GLsizei, + height: GLsizei, + format: GLenum, + pixel_type: GLenum, + ) { + panic!(); + } + + fn sample_coverage(&self, value: GLclampf, invert: bool) { + panic!(); + } + + fn polygon_offset(&self, factor: GLfloat, units: GLfloat) { + panic!(); + } + + fn pixel_store_i(&self, name: GLenum, param: GLint) { + //panic!(); + debug!("pixel_store_i {:x} {}", name, param); + unsafe { + PixelStorei(name, param); + } + } + + fn gen_buffers(&self, n: GLsizei) -> Vec { + //panic!(); + let mut result = vec![0 as GLuint; n as usize]; + unsafe { + GenBuffers(n, result.as_mut_ptr()); + } + result + } + + fn gen_renderbuffers(&self, n: GLsizei) -> Vec { + debug!("gen_renderbuffers {}", n); + //panic!(); + let mut result = vec![0 as GLuint; n as usize]; + unsafe { + GenRenderbuffers(n, result.as_mut_ptr()); + } + result + } + + fn gen_framebuffers(&self, n: GLsizei) -> Vec { + //panic!(); + debug!("gen_framebuffers {}", n); + let mut result = vec![0 as GLuint; n as usize]; + unsafe { + GenFramebuffers(n, result.as_mut_ptr()); + } + result + } + + fn gen_textures(&self, n: GLsizei) -> Vec { + //panic!(); + let mut result = vec![0 as GLuint; n as usize]; + unsafe { + GenTextures(n, result.as_mut_ptr()); + } + result + } + + fn gen_vertex_arrays(&self, n: GLsizei) -> Vec { + //panic!(); + let mut result = vec![0 as GLuint; n as usize]; + unsafe { + GenVertexArrays(n, result.as_mut_ptr()); + } + result + } + + fn gen_vertex_arrays_apple(&self, n: GLsizei) -> Vec { + self.gen_vertex_arrays(n) + } + + fn gen_queries(&self, n: GLsizei) -> Vec { + let mut result = vec![0 as GLuint; n as usize]; + unsafe { + GenQueries(n, result.as_mut_ptr()); + } + result + } + + fn begin_query(&self, target: GLenum, id: GLuint) { + unsafe { + BeginQuery(target, id); + } + } + + fn end_query(&self, target: GLenum) { + unsafe { + EndQuery(target); + } + } + + fn query_counter(&self, id: GLuint, target: GLenum) { + panic!(); + } + + fn get_query_object_iv(&self, id: GLuint, pname: GLenum) -> i32 { + panic!(); + //0 + } + + fn get_query_object_uiv(&self, id: GLuint, pname: GLenum) -> u32 { + panic!(); + //0 + } + + fn get_query_object_i64v(&self, id: GLuint, pname: GLenum) -> i64 { + panic!(); + //0 + } + + fn get_query_object_ui64v(&self, id: GLuint, pname: GLenum) -> u64 { + let mut result = 0; + unsafe { + GetQueryObjectui64v(id, pname, &mut result); + } + result + } + + fn delete_queries(&self, queries: &[GLuint]) { + unsafe { + for q in queries { + DeleteQuery(*q); + } + } + } + + fn delete_vertex_arrays(&self, vertex_arrays: &[GLuint]) { + unsafe { + for v in vertex_arrays { + DeleteVertexArray(*v); + } + } + } + + fn delete_vertex_arrays_apple(&self, vertex_arrays: &[GLuint]) { + self.delete_vertex_arrays(vertex_arrays) + } + + fn delete_buffers(&self, buffers: &[GLuint]) { + unsafe { + for b in buffers { + DeleteBuffer(*b); + } + } + } + + fn delete_renderbuffers(&self, renderbuffers: &[GLuint]) { + unsafe { + for r in renderbuffers { + DeleteRenderbuffer(*r); + } + } + } + + fn delete_framebuffers(&self, framebuffers: &[GLuint]) { + unsafe { + for f in framebuffers { + DeleteFramebuffer(*f); + } + } + } + + fn delete_textures(&self, textures: &[GLuint]) { + unsafe { + for t in textures { + DeleteTexture(*t); + } + } + } + + fn framebuffer_renderbuffer( + &self, + target: GLenum, + attachment: GLenum, + renderbuffertarget: GLenum, + renderbuffer: GLuint, + ) { + debug!( + "framebufer_renderbuffer {} {} {} {}", + target, attachment, renderbuffertarget, renderbuffer + ); + //panic!(); + unsafe { + FramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer); + } + } + + fn renderbuffer_storage( + &self, + target: GLenum, + internalformat: GLenum, + width: GLsizei, + height: GLsizei, + ) { + debug!( + "renderbuffer_storage {} {} {} {}", + target, internalformat, width, height + ); + //panic!(); + unsafe { + RenderbufferStorage(target, internalformat, width, height); + } + } + + fn depth_func(&self, func: GLenum) { + debug!("depth_func {}", func); + //panic!(); + unsafe { + DepthFunc(func); + } + } + + fn active_texture(&self, texture: GLenum) { + //panic!(); + unsafe { + ActiveTexture(texture); + } + } + + fn attach_shader(&self, program: GLuint, shader: GLuint) { + debug!("attach shader {} {}", program, shader); + //panic!(); + unsafe { + AttachShader(program, shader); + } + } + + fn bind_attrib_location(&self, program: GLuint, index: GLuint, name: &str) { + debug!("bind_attrib_location {} {} {}", program, index, name); + //panic!(); + let c_string = CString::new(name).unwrap(); + unsafe { BindAttribLocation(program, index, c_string.as_ptr()) } + } + + // https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glGetUniform.xml + unsafe fn get_uniform_iv(&self, program: GLuint, location: GLint, result: &mut [GLint]) { + panic!(); + //assert!(!result.is_empty()); + } + + // https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glGetUniform.xml + unsafe fn get_uniform_fv(&self, program: GLuint, location: GLint, result: &mut [GLfloat]) { + panic!(); + //assert!(!result.is_empty()); + } + + fn get_uniform_block_index(&self, program: GLuint, name: &str) -> GLuint { + panic!(); + //0 + } + + fn get_uniform_indices(&self, program: GLuint, names: &[&str]) -> Vec { + panic!(); + //Vec::new() + } + + fn bind_buffer_base(&self, target: GLenum, index: GLuint, buffer: GLuint) { + panic!(); + } + + fn bind_buffer_range( + &self, + target: GLenum, + index: GLuint, + buffer: GLuint, + offset: GLintptr, + size: GLsizeiptr, + ) { + panic!(); + } + + fn uniform_block_binding( + &self, + program: GLuint, + uniform_block_index: GLuint, + uniform_block_binding: GLuint, + ) { + panic!(); + } + + fn bind_buffer(&self, target: GLenum, buffer: GLuint) { + //panic!(); + unsafe { + BindBuffer(target, buffer); + } + } + + fn bind_vertex_array(&self, vao: GLuint) { + //panic!(); + unsafe { + BindVertexArray(vao); + } + } + + fn bind_vertex_array_apple(&self, vao: GLuint) { + self.bind_vertex_array(vao) + } + + fn bind_renderbuffer(&self, target: GLenum, renderbuffer: GLuint) { + debug!("bind_renderbuffer {} {}", target, renderbuffer); + //panic!(); + unsafe { + BindRenderbuffer(target, renderbuffer); + } + } + + fn bind_framebuffer(&self, target: GLenum, framebuffer: GLuint) { + debug!("bind_framebuffer {} {}", target, framebuffer); + //panic!(); + unsafe { + BindFramebuffer(target, framebuffer); + } + } + + fn bind_texture(&self, target: GLenum, texture: GLuint) { + //panic!(); + unsafe { + BindTexture(target, texture); + } + } + + fn draw_buffers(&self, bufs: &[GLenum]) { + panic!(); + //unsafe {} + } + + // FIXME: Does not verify buffer size -- unsafe! + fn tex_image_2d( + &self, + target: GLenum, + level: GLint, + internal_format: GLint, + width: GLsizei, + height: GLsizei, + border: GLint, + format: GLenum, + ty: GLenum, + opt_data: Option<&[u8]>, + ) { + unsafe { + let pdata = match opt_data { + Some(data) => data.as_ptr() as *const GLvoid, + None => ptr::null(), + }; + TexImage2D( + target, + level, + internal_format, + width, + height, + border, + format, + ty, + pdata, + ); + } + } + + fn compressed_tex_image_2d( + &self, + target: GLenum, + level: GLint, + internal_format: GLenum, + width: GLsizei, + height: GLsizei, + border: GLint, + data: &[u8], + ) { + panic!(); + } + + fn compressed_tex_sub_image_2d( + &self, + target: GLenum, + level: GLint, + xoffset: GLint, + yoffset: GLint, + width: GLsizei, + height: GLsizei, + format: GLenum, + data: &[u8], + ) { + panic!(); + } + + // FIXME: Does not verify buffer size -- unsafe! + fn tex_image_3d( + &self, + target: GLenum, + level: GLint, + internal_format: GLint, + width: GLsizei, + height: GLsizei, + depth: GLsizei, + border: GLint, + format: GLenum, + ty: GLenum, + opt_data: Option<&[u8]>, + ) { + unsafe { + let pdata = match opt_data { + Some(data) => data.as_ptr() as *const GLvoid, + None => ptr::null(), + }; + TexImage3D( + target, + level, + internal_format, + width, + height, + depth, + border, + format, + ty, + pdata, + ); + } + } + + fn copy_tex_image_2d( + &self, + target: GLenum, + level: GLint, + internal_format: GLenum, + x: GLint, + y: GLint, + width: GLsizei, + height: GLsizei, + border: GLint, + ) { + panic!(); + } + + fn copy_tex_sub_image_2d( + &self, + target: GLenum, + level: GLint, + xoffset: GLint, + yoffset: GLint, + x: GLint, + y: GLint, + width: GLsizei, + height: GLsizei, + ) { + unsafe { + CopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height); + } + } + + fn copy_tex_sub_image_3d( + &self, + target: GLenum, + level: GLint, + xoffset: GLint, + yoffset: GLint, + zoffset: GLint, + x: GLint, + y: GLint, + width: GLsizei, + height: GLsizei, + ) { + unsafe { + CopyTexSubImage3D( + target, level, xoffset, yoffset, zoffset, x, y, width, height, + ); + } + } + + fn tex_sub_image_2d( + &self, + target: GLenum, + level: GLint, + xoffset: GLint, + yoffset: GLint, + width: GLsizei, + height: GLsizei, + format: GLenum, + ty: GLenum, + data: &[u8], + ) { + debug!( + "tex_sub_image_2d {} {} {} {} {} {} {} {}", + target, level, xoffset, yoffset, width, height, format, ty + ); + //panic!(); + unsafe { + TexSubImage2D( + target, + level, + xoffset, + yoffset, + width, + height, + format, + ty, + data.as_ptr() as *const c_void, + ); + } + } + + fn tex_sub_image_2d_pbo( + &self, + target: GLenum, + level: GLint, + xoffset: GLint, + yoffset: GLint, + width: GLsizei, + height: GLsizei, + format: GLenum, + ty: GLenum, + offset: usize, + ) { + debug!( + "tex_sub_image_2d_pbo {} {} {} {} {} {} {} {} {}", + target, level, xoffset, yoffset, width, height, format, ty, offset + ); + //panic!(); + unsafe { + TexSubImage2D( + target, + level, + xoffset, + yoffset, + width, + height, + format, + ty, + offset as *const c_void, + ); + } + } + + fn tex_sub_image_3d( + &self, + target: GLenum, + level: GLint, + xoffset: GLint, + yoffset: GLint, + zoffset: GLint, + width: GLsizei, + height: GLsizei, + depth: GLsizei, + format: GLenum, + ty: GLenum, + data: &[u8], + ) { + debug!("tex_sub_image_3d"); + //panic!(); + unsafe { + TexSubImage3D( + target, + level, + xoffset, + yoffset, + zoffset, + width, + height, + depth, + format, + ty, + data.as_ptr() as *const c_void, + ); + } + } + + fn tex_sub_image_3d_pbo( + &self, + target: GLenum, + level: GLint, + xoffset: GLint, + yoffset: GLint, + zoffset: GLint, + width: GLsizei, + height: GLsizei, + depth: GLsizei, + format: GLenum, + ty: GLenum, + offset: usize, + ) { + unsafe { + TexSubImage3D( + target, + level, + xoffset, + yoffset, + zoffset, + width, + height, + depth, + format, + ty, + offset as *const c_void, + ); + } + } + + fn tex_storage_2d( + &self, + target: GLenum, + levels: GLint, + internal_format: GLenum, + width: GLsizei, + height: GLsizei, + ) { + //panic!(); + unsafe { + TexStorage2D(target, levels, internal_format, width, height); + } + } + + fn tex_storage_3d( + &self, + target: GLenum, + levels: GLint, + internal_format: GLenum, + width: GLsizei, + height: GLsizei, + depth: GLsizei, + ) { + //panic!(); + unsafe { + TexStorage3D(target, levels, internal_format, width, height, depth); + } + } + + fn get_tex_image_into_buffer( + &self, + target: GLenum, + level: GLint, + format: GLenum, + ty: GLenum, + output: &mut [u8], + ) { + panic!(); + } + + unsafe fn copy_image_sub_data( + &self, + src_name: GLuint, + src_target: GLenum, + src_level: GLint, + src_x: GLint, + src_y: GLint, + src_z: GLint, + dst_name: GLuint, + dst_target: GLenum, + dst_level: GLint, + dst_x: GLint, + dst_y: GLint, + dst_z: GLint, + src_width: GLsizei, + src_height: GLsizei, + src_depth: GLsizei, + ) { + CopyImageSubData( + src_name, src_target, src_level, src_x, src_y, src_z, dst_name, dst_target, dst_level, + dst_x, dst_y, dst_z, src_width, src_height, src_depth, + ); + } + + fn invalidate_framebuffer(&self, target: GLenum, attachments: &[GLenum]) { + unsafe { + InvalidateFramebuffer(target, attachments.len() as GLsizei, attachments.as_ptr()); + } + } + + fn invalidate_sub_framebuffer( + &self, + target: GLenum, + attachments: &[GLenum], + xoffset: GLint, + yoffset: GLint, + width: GLsizei, + height: GLsizei, + ) { + } + + #[inline] + unsafe fn get_integer_v(&self, name: GLenum, result: &mut [GLint]) { + //panic!(); + assert!(!result.is_empty()); + GetIntegerv(name, result.as_mut_ptr()); + } + + #[inline] + unsafe fn get_integer_64v(&self, name: GLenum, result: &mut [GLint64]) { + panic!(); + //assert!(!result.is_empty()); + } + + #[inline] + unsafe fn get_integer_iv(&self, name: GLenum, index: GLuint, result: &mut [GLint]) { + panic!(); + //assert!(!result.is_empty()); + } + + #[inline] + unsafe fn get_integer_64iv(&self, name: GLenum, index: GLuint, result: &mut [GLint64]) { + panic!(); + //assert!(!result.is_empty()); + } + + #[inline] + unsafe fn get_boolean_v(&self, name: GLenum, result: &mut [GLboolean]) { + debug!("get_boolean_v {}", name); + //panic!(); + assert!(!result.is_empty()); + GetBooleanv(name, result.as_mut_ptr()); + } + + #[inline] + unsafe fn get_float_v(&self, name: GLenum, result: &mut [GLfloat]) { + panic!(); + //assert!(!result.is_empty()); + } + + fn get_framebuffer_attachment_parameter_iv( + &self, + target: GLenum, + attachment: GLenum, + pname: GLenum, + ) -> GLint { + panic!(); + //0 + } + + fn get_renderbuffer_parameter_iv(&self, target: GLenum, pname: GLenum) -> GLint { + panic!(); + //0 + } + + fn get_tex_parameter_iv(&self, target: GLenum, pname: GLenum) -> GLint { + panic!(); + //0 + } + + fn get_tex_parameter_fv(&self, target: GLenum, pname: GLenum) -> GLfloat { + panic!(); + //0.0 + } + + fn tex_parameter_i(&self, target: GLenum, pname: GLenum, param: GLint) { + //panic!(); + unsafe { + TexParameteri(target, pname, param); + } + } + + fn tex_parameter_f(&self, target: GLenum, pname: GLenum, param: GLfloat) { + panic!(); + } + + fn framebuffer_texture_2d( + &self, + target: GLenum, + attachment: GLenum, + textarget: GLenum, + texture: GLuint, + level: GLint, + ) { + debug!( + "framebuffer_texture_2d {} {} {} {} {}", + target, attachment, textarget, texture, level + ); + //panic!(); + unsafe { + FramebufferTexture2D(target, attachment, textarget, texture, level); + } + } + + fn framebuffer_texture_layer( + &self, + target: GLenum, + attachment: GLenum, + texture: GLuint, + level: GLint, + layer: GLint, + ) { + debug!( + "framebuffer_texture_layer {} {} {} {} {}", + target, attachment, texture, level, layer + ); + //panic!(); + unsafe { + FramebufferTextureLayer(target, attachment, texture, level, layer); + } + } + + fn blit_framebuffer( + &self, + src_x0: GLint, + src_y0: GLint, + src_x1: GLint, + src_y1: GLint, + dst_x0: GLint, + dst_y0: GLint, + dst_x1: GLint, + dst_y1: GLint, + mask: GLbitfield, + filter: GLenum, + ) { + unsafe { + BlitFramebuffer( + src_x0, src_y0, src_x1, src_y1, dst_x0, dst_y0, dst_x1, dst_y1, mask, filter, + ); + } + } + + fn vertex_attrib_4f(&self, index: GLuint, x: GLfloat, y: GLfloat, z: GLfloat, w: GLfloat) { + panic!(); + } + + fn vertex_attrib_pointer_f32( + &self, + index: GLuint, + size: GLint, + normalized: bool, + stride: GLsizei, + offset: GLuint, + ) { + panic!(); + } + + fn vertex_attrib_pointer( + &self, + index: GLuint, + size: GLint, + type_: GLenum, + normalized: bool, + stride: GLsizei, + offset: GLuint, + ) { + debug!( + "vertex_attrib_pointer {} {} {} {} {} {}", + index, size, type_, normalized, stride, offset + ); + //panic!(); + unsafe { + VertexAttribPointer( + index, + size, + type_, + normalized as GLboolean, + stride, + offset as *const GLvoid, + ); + } + } + + fn vertex_attrib_i_pointer( + &self, + index: GLuint, + size: GLint, + type_: GLenum, + stride: GLsizei, + offset: GLuint, + ) { + debug!( + "vertex_attrib_i_pointer {} {} {} {} {}", + index, size, type_, stride, offset + ); + //panic!(); + unsafe { + VertexAttribIPointer(index, size, type_, stride, offset as *const GLvoid); + } + } + + fn vertex_attrib_divisor(&self, index: GLuint, divisor: GLuint) { + debug!("vertex_attrib_divisor {} {}", index, divisor); + //assert!(index == 0 && divisor == 0); + //panic!(); + unsafe { + VertexAttribDivisor(index, divisor); + } + } + + fn viewport(&self, x: GLint, y: GLint, width: GLsizei, height: GLsizei) { + debug!("viewport {} {} {} {}", x, y, width, height); + //panic!(); + unsafe { + SetViewport(x, y, width, height); + } + } + + fn scissor(&self, x: GLint, y: GLint, width: GLsizei, height: GLsizei) { + //panic!(); + unsafe { + SetScissor(x, y, width, height); + } + } + + fn line_width(&self, width: GLfloat) { + panic!(); + } + + fn use_program(&self, program: GLuint) { + //panic!(); + unsafe { + UseProgram(program); + } + } + + fn validate_program(&self, program: GLuint) { + panic!(); + } + + fn draw_arrays(&self, mode: GLenum, first: GLint, count: GLsizei) { + panic!(); + } + + fn draw_arrays_instanced( + &self, + mode: GLenum, + first: GLint, + count: GLsizei, + primcount: GLsizei, + ) { + panic!(); + } + + fn draw_elements( + &self, + mode: GLenum, + count: GLsizei, + element_type: GLenum, + indices_offset: GLuint, + ) { + debug!( + "draw_elements {} {} {} {} {}", + mode, count, element_type, indices_offset + ); + //panic!(); + unsafe { + DrawElementsInstanced( + mode, + count, + element_type, + indices_offset as *const c_void, + 1, + ); + } + } + + fn draw_elements_instanced( + &self, + mode: GLenum, + count: GLsizei, + element_type: GLenum, + indices_offset: GLuint, + primcount: GLsizei, + ) { + debug!( + "draw_elements_instanced {} {} {} {} {}", + mode, count, element_type, indices_offset, primcount + ); + //panic!(); + unsafe { + DrawElementsInstanced( + mode, + count, + element_type, + indices_offset as *const c_void, + primcount, + ); + } + } + + fn blend_color(&self, r: f32, g: f32, b: f32, a: f32) { + unsafe { + BlendColor(r, g, b, a); + } + } + + fn blend_func(&self, sfactor: GLenum, dfactor: GLenum) { + unsafe { + BlendFunc(sfactor, dfactor, sfactor, dfactor); + } + } + + fn blend_func_separate( + &self, + src_rgb: GLenum, + dest_rgb: GLenum, + src_alpha: GLenum, + dest_alpha: GLenum, + ) { + unsafe { + BlendFunc(src_rgb, dest_rgb, src_alpha, dest_alpha); + } + } + + fn blend_equation(&self, mode: GLenum) { + unsafe { + BlendEquation(mode); + } + } + + fn blend_equation_separate(&self, mode_rgb: GLenum, mode_alpha: GLenum) { + panic!(); + } + + fn color_mask(&self, r: bool, g: bool, b: bool, a: bool) { + panic!(); + } + + fn cull_face(&self, mode: GLenum) { + panic!(); + } + + fn front_face(&self, mode: GLenum) { + panic!(); + } + + fn enable(&self, cap: GLenum) { + debug!("enable {}", cap); + //panic!(); + unsafe { + Enable(cap); + } + } + + fn disable(&self, cap: GLenum) { + debug!("disable {}", cap); + //panic!(); + unsafe { + Disable(cap); + } + } + + fn hint(&self, param_name: GLenum, param_val: GLenum) { + panic!(); + } + + fn is_enabled(&self, cap: GLenum) -> GLboolean { + panic!(); + //0 + } + + fn is_shader(&self, shader: GLuint) -> GLboolean { + panic!(); + //0 + } + + fn is_texture(&self, texture: GLenum) -> GLboolean { + panic!(); + //0 + } + + fn is_framebuffer(&self, framebuffer: GLenum) -> GLboolean { + panic!(); + //0 + } + + fn is_renderbuffer(&self, renderbuffer: GLenum) -> GLboolean { + panic!(); + //0 + } + + fn check_frame_buffer_status(&self, target: GLenum) -> GLenum { + debug!("check_frame_buffer_status {}", target); + //panic!(); + unsafe { CheckFramebufferStatus(target) } + } + + fn enable_vertex_attrib_array(&self, index: GLuint) { + //panic!(); + debug!("enable_vertex_attrib_array {}", index); + unsafe { + EnableVertexAttribArray(index); + //assert_eq!(index, 0); + } + } + + fn disable_vertex_attrib_array(&self, index: GLuint) { + panic!(); + } + + fn uniform_1f(&self, location: GLint, v0: GLfloat) { + panic!(); + } + + fn uniform_1fv(&self, location: GLint, values: &[f32]) { + panic!(); + } + + fn uniform_1i(&self, location: GLint, v0: GLint) { + debug!("uniform_1i {} {}", location, v0); + //panic!(); + unsafe { + Uniform1i(location, v0); + } + } + + fn uniform_1iv(&self, location: GLint, values: &[i32]) { + panic!(); + } + + fn uniform_1ui(&self, location: GLint, v0: GLuint) { + panic!(); + } + + fn uniform_2f(&self, location: GLint, v0: GLfloat, v1: GLfloat) { + panic!(); + } + + fn uniform_2fv(&self, location: GLint, values: &[f32]) { + panic!(); + } + + fn uniform_2i(&self, location: GLint, v0: GLint, v1: GLint) { + panic!(); + } + + fn uniform_2iv(&self, location: GLint, values: &[i32]) { + panic!(); + } + + fn uniform_2ui(&self, location: GLint, v0: GLuint, v1: GLuint) { + panic!(); + } + + fn uniform_3f(&self, location: GLint, v0: GLfloat, v1: GLfloat, v2: GLfloat) { + panic!(); + } + + fn uniform_3fv(&self, location: GLint, values: &[f32]) { + panic!(); + } + + fn uniform_3i(&self, location: GLint, v0: GLint, v1: GLint, v2: GLint) { + panic!(); + } + + fn uniform_3iv(&self, location: GLint, values: &[i32]) { + panic!(); + } + + fn uniform_3ui(&self, location: GLint, v0: GLuint, v1: GLuint, v2: GLuint) { + panic!(); + } + + fn uniform_4f(&self, location: GLint, x: GLfloat, y: GLfloat, z: GLfloat, w: GLfloat) { + panic!(); + } + + fn uniform_4i(&self, location: GLint, x: GLint, y: GLint, z: GLint, w: GLint) { + panic!(); + } + + fn uniform_4iv(&self, location: GLint, values: &[i32]) { + panic!(); + } + + fn uniform_4ui(&self, location: GLint, x: GLuint, y: GLuint, z: GLuint, w: GLuint) { + panic!(); + } + + fn uniform_4fv(&self, location: GLint, values: &[f32]) { + unsafe { + Uniform4fv(location, (values.len() / 4) as GLsizei, values.as_ptr()); + } + } + + fn uniform_matrix_2fv(&self, location: GLint, transpose: bool, value: &[f32]) { + panic!(); + } + + fn uniform_matrix_3fv(&self, location: GLint, transpose: bool, value: &[f32]) { + panic!(); + } + + fn uniform_matrix_4fv(&self, location: GLint, transpose: bool, value: &[f32]) { + debug!("uniform_matrix_4fv {} {} {:?}", location, transpose, value); + //panic!(); + unsafe { + UniformMatrix4fv( + location, + (value.len() / 16) as GLsizei, + transpose as GLboolean, + value.as_ptr(), + ); + } + } + + fn depth_mask(&self, flag: bool) { + debug!("depth_mask {}", flag); + //panic!(); + unsafe { + DepthMask(flag as GLboolean); + } + } + + fn depth_range(&self, near: f64, far: f64) { + panic!(); + } + + fn get_active_attrib(&self, program: GLuint, index: GLuint) -> (i32, u32, String) { + panic!(); + //(0, 0, String::new()) + } + + fn get_active_uniform(&self, program: GLuint, index: GLuint) -> (i32, u32, String) { + panic!(); + //(0, 0, String::new()) + } + + fn get_active_uniforms_iv( + &self, + program: GLuint, + indices: Vec, + pname: GLenum, + ) -> Vec { + panic!(); + //Vec::new() + } + + fn get_active_uniform_block_i(&self, program: GLuint, index: GLuint, pname: GLenum) -> GLint { + panic!(); + //0 + } + + fn get_active_uniform_block_iv( + &self, + program: GLuint, + index: GLuint, + pname: GLenum, + ) -> Vec { + panic!(); + //Vec::new() + } + + fn get_active_uniform_block_name(&self, program: GLuint, index: GLuint) -> String { + panic!(); + //String::new() + } + + fn get_attrib_location(&self, program: GLuint, name: &str) -> c_int { + let name = CString::new(name).unwrap(); + unsafe { GetAttribLocation(program, name.as_ptr()) } + } + + fn get_frag_data_location(&self, program: GLuint, name: &str) -> c_int { + panic!(); + //0 + } + + fn get_uniform_location(&self, program: GLuint, name: &str) -> c_int { + debug!("get_uniform_location {} {}", program, name); + //panic!(); + let name = CString::new(name).unwrap(); + unsafe { GetUniformLocation(program, name.as_ptr()) } + } + + fn get_program_info_log(&self, program: GLuint) -> String { + panic!(); + //String::new() + } + + #[inline] + unsafe fn get_program_iv(&self, program: GLuint, pname: GLenum, result: &mut [GLint]) { + debug!("get_program_iv {}", pname); + //panic!(); + assert!(!result.is_empty()); + //#define GL_LINK_STATUS 0x8B82 + if pname == 0x8b82 { + result[0] = 1; + } + } + + fn get_program_binary(&self, program: GLuint) -> (Vec, GLenum) { + panic!(); + //(Vec::new(), NONE) + } + + fn program_binary(&self, program: GLuint, format: GLenum, binary: &[u8]) { + panic!(); + } + + fn program_parameter_i(&self, program: GLuint, pname: GLenum, value: GLint) { + panic!(); + } + + #[inline] + unsafe fn get_vertex_attrib_iv(&self, index: GLuint, pname: GLenum, result: &mut [GLint]) { + panic!(); + //assert!(!result.is_empty()); + } + + #[inline] + unsafe fn get_vertex_attrib_fv(&self, index: GLuint, pname: GLenum, result: &mut [GLfloat]) { + panic!(); + //assert!(!result.is_empty()); + } + + fn get_vertex_attrib_pointer_v(&self, index: GLuint, pname: GLenum) -> GLsizeiptr { + panic!(); + //0 + } + + fn get_buffer_parameter_iv(&self, target: GLuint, pname: GLenum) -> GLint { + panic!(); + //0 + } + + fn get_shader_info_log(&self, shader: GLuint) -> String { + debug!("get_shader_info_log {}", shader); + //panic!(); + String::new() + } + + fn get_string(&self, which: GLenum) -> String { + // panic!(); + unsafe { + let llstr = GetString(which); + if !llstr.is_null() { + return str::from_utf8_unchecked(CStr::from_ptr(llstr).to_bytes()).to_string(); + } else { + return "".to_string(); + } + } + } + + fn get_string_i(&self, which: GLenum, index: GLuint) -> String { + //panic!(); + unsafe { + let llstr = GetStringi(which, index); + if !llstr.is_null() { + str::from_utf8_unchecked(CStr::from_ptr(llstr).to_bytes()).to_string() + } else { + "".to_string() + } + } + } + + unsafe fn get_shader_iv(&self, shader: GLuint, pname: GLenum, result: &mut [GLint]) { + debug!("get_shader_iv"); + //panic!(); + assert!(!result.is_empty()); + if pname == 0x8B81 + /*gl::COMPILE_STATUS*/ + { + result[0] = 1; + } + } + + fn get_shader_precision_format( + &self, + _shader_type: GLuint, + precision_type: GLuint, + ) -> (GLint, GLint, GLint) { + // gl.GetShaderPrecisionFormat is not available until OpenGL 4.1. + // Fallback to OpenGL standard precissions that most desktop hardware support. + match precision_type { + LOW_FLOAT | MEDIUM_FLOAT | HIGH_FLOAT => { + // Fallback to IEEE 754 single precision + // Range: from -2^127 to 2^127 + // Significand precision: 23 bits + (127, 127, 23) + } + LOW_INT | MEDIUM_INT | HIGH_INT => { + // Fallback to single precision integer + // Range: from -2^24 to 2^24 + // Precision: For integer formats this value is always 0 + (24, 24, 0) + } + _ => (0, 0, 0), + } + } + + fn compile_shader(&self, shader: GLuint) { + debug!("compile_shader {}", shader); + //panic!(); + } + + fn create_program(&self) -> GLuint { + debug!("create_program"); + //panic!(); + unsafe { CreateProgram() } + } + + fn delete_program(&self, program: GLuint) { + unsafe { + DeleteProgram(program); + } + } + + fn create_shader(&self, shader_type: GLenum) -> GLuint { + debug!("create_shader {}", shader_type); + //panic!(); + unsafe { CreateShader(shader_type) } + } + + fn delete_shader(&self, shader: GLuint) { + debug!("delete_shader {}", shader); + //panic!(); + unsafe { + DeleteShader(shader); + } + } + + fn detach_shader(&self, program: GLuint, shader: GLuint) { + debug!("detach_shader {} {}", program, shader); + //panic!(); + } + + fn link_program(&self, program: GLuint) { + debug!("link_program {}", program); + //panic!(); + unsafe { + LinkProgram(program); + } + } + + fn clear_color(&self, r: f32, g: f32, b: f32, a: f32) { + //panic!(); + unsafe { + ClearColor(r, g, b, a); + } + } + + fn clear(&self, buffer_mask: GLbitfield) { + debug!("clear {}", buffer_mask); + //panic!(); + unsafe { + Clear(buffer_mask); + } + } + + fn clear_depth(&self, depth: f64) { + debug!("clear_depth {}", depth); + //panic!(); + unsafe { + ClearDepth(depth as GLclampd); + } + } + + fn clear_stencil(&self, s: GLint) { + panic!(); + } + + fn flush(&self) {} + + fn finish(&self) { + unsafe { + Finish(); + } + } + + fn get_error(&self) -> GLenum { + //panic!(); + unsafe { GetError() } + } + + fn stencil_mask(&self, mask: GLuint) { + panic!(); + } + + fn stencil_mask_separate(&self, face: GLenum, mask: GLuint) { + panic!(); + } + + fn stencil_func(&self, func: GLenum, ref_: GLint, mask: GLuint) { + panic!(); + } + + fn stencil_func_separate(&self, face: GLenum, func: GLenum, ref_: GLint, mask: GLuint) { + panic!(); + } + + fn stencil_op(&self, sfail: GLenum, dpfail: GLenum, dppass: GLenum) { + panic!(); + } + + fn stencil_op_separate(&self, face: GLenum, sfail: GLenum, dpfail: GLenum, dppass: GLenum) { + panic!(); + } + + fn egl_image_target_texture2d_oes(&self, target: GLenum, image: GLeglImageOES) { + panic!("not supported") + } + + fn egl_image_target_renderbuffer_storage_oes(&self, target: GLenum, image: GLeglImageOES) { + panic!("not supported") + } + + fn generate_mipmap(&self, target: GLenum) { + panic!(); + } + + fn insert_event_marker_ext(&self, message: &str) { + panic!(); + } + + fn push_group_marker_ext(&self, message: &str) { + debug!("push group {}", message); + panic!(); + } + + fn pop_group_marker_ext(&self) { + debug!("pop group"); + panic!(); + } + + fn debug_message_insert_khr( + &self, + source: GLenum, + type_: GLenum, + id: GLuint, + severity: GLenum, + message: &str, + ) { + panic!(); + } + + fn push_debug_group_khr(&self, source: GLenum, id: GLuint, message: &str) { + panic!(); + } + + fn pop_debug_group_khr(&self) { + panic!(); + } + + fn fence_sync(&self, condition: GLenum, flags: GLbitfield) -> GLsync { + panic!(); + //ptr::null() + } + + fn client_wait_sync(&self, sync: GLsync, flags: GLbitfield, timeout: GLuint64) { + panic!(); + } + + fn wait_sync(&self, sync: GLsync, flags: GLbitfield, timeout: GLuint64) { + panic!(); + } + + fn texture_range_apple(&self, target: GLenum, data: &[u8]) { + panic!(); + } + + fn delete_sync(&self, sync: GLsync) { + panic!(); + } + + fn gen_fences_apple(&self, n: GLsizei) -> Vec { + panic!(); + //Vec::new() + } + + fn delete_fences_apple(&self, fences: &[GLuint]) { + panic!(); + } + + fn set_fence_apple(&self, fence: GLuint) { + panic!(); + } + + fn finish_fence_apple(&self, fence: GLuint) { + panic!(); + } + + fn test_fence_apple(&self, fence: GLuint) { + panic!(); + } + + fn test_object_apple(&self, object: GLenum, name: GLuint) -> GLboolean { + panic!(); + //0 + } + + fn finish_object_apple(&self, object: GLenum, name: GLuint) { + panic!(); + } + + // GL_ARB_blend_func_extended + fn bind_frag_data_location_indexed( + &self, + program: GLuint, + color_number: GLuint, + index: GLuint, + name: &str, + ) { + panic!(); + } + + fn get_frag_data_index(&self, program: GLuint, name: &str) -> GLint { + panic!(); + //-1 + } + + // GL_KHR_debug + fn get_debug_messages(&self) -> Vec { + Vec::new() + } + + fn provoking_vertex_angle(&self, _mode: GLenum) { + unimplemented!("This extension is GLES only"); + } + + // GL_KHR_blend_equation_advanced + fn blend_barrier_khr(&self) { + panic!(); + } + + // GL_CHROMIUM_copy_texture + fn copy_texture_chromium( + &self, + _source_id: GLuint, + _source_level: GLint, + _dest_target: GLenum, + _dest_id: GLuint, + _dest_level: GLint, + _internal_format: GLint, + _dest_type: GLenum, + _unpack_flip_y: GLboolean, + _unpack_premultiply_alpha: GLboolean, + _unpack_unmultiply_alpha: GLboolean, + ) { + unimplemented!("This extension is GLES only"); + } + fn copy_sub_texture_chromium( + &self, + _source_id: GLuint, + _source_level: GLint, + _dest_target: GLenum, + _dest_id: GLuint, + _dest_level: GLint, + _x_offset: GLint, + _y_offset: GLint, + _x: GLint, + _y: GLint, + _width: GLsizei, + _height: GLsizei, + _unpack_flip_y: GLboolean, + _unpack_premultiply_alpha: GLboolean, + _unpack_unmultiply_alpha: GLboolean, + ) { + unimplemented!("This extension is GLES only"); + } +} diff --git a/swgl/src/texture.h b/swgl/src/texture.h new file mode 100644 index 0000000000..3ec0136c9c --- /dev/null +++ b/swgl/src/texture.h @@ -0,0 +1,121 @@ +/* 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/. */ + +template +void textureLinearCommit4(S sampler, ivec2 i, int zoffset, uint32_t* buf) { + assert(sampler->format == TextureFormat::RGBA8); + ivec2 frac = i & 0x7F; + i >>= 7; + + I32 row0 = clampCoord(i.x, sampler->width) + + clampCoord(i.y, sampler->height) * sampler->stride + zoffset; + I32 row1 = row0 + ((i.y > 0 && i.y < int32_t(sampler->height) - 1) & + I32(sampler->stride)); + I16 fracx = + CONVERT(frac.x & (i.x > 0 && i.x < int32_t(sampler->width) - 1), I16); + I16 fracy = CONVERT(frac.y, I16); + + auto a0 = + CONVERT(unaligned_load>(&sampler->buf[row0.x]), V8); + auto a1 = + CONVERT(unaligned_load>(&sampler->buf[row1.x]), V8); + a0 += ((a1 - a0) * fracy.x) >> 7; + + auto b0 = + CONVERT(unaligned_load>(&sampler->buf[row0.y]), V8); + auto b1 = + CONVERT(unaligned_load>(&sampler->buf[row1.y]), V8); + b0 += ((b1 - b0) * fracy.y) >> 7; + + auto abl = combine(lowHalf(a0), lowHalf(b0)); + auto abh = combine(highHalf(a0), highHalf(b0)); + abl += ((abh - abl) * fracx.xxxxyyyy) >> 7; + + auto c0 = + CONVERT(unaligned_load>(&sampler->buf[row0.z]), V8); + auto c1 = + CONVERT(unaligned_load>(&sampler->buf[row1.z]), V8); + c0 += ((c1 - c0) * fracy.z) >> 7; + + auto d0 = + CONVERT(unaligned_load>(&sampler->buf[row0.w]), V8); + auto d1 = + CONVERT(unaligned_load>(&sampler->buf[row1.w]), V8); + d0 += ((d1 - d0) * fracy.w) >> 7; + + auto cdl = combine(lowHalf(c0), lowHalf(d0)); + auto cdh = combine(highHalf(c0), highHalf(d0)); + cdl += ((cdh - cdl) * fracx.zzzzwwww) >> 7; + + commit_span(buf, pack(combine(HalfRGBA8(abl), HalfRGBA8(cdl)))); +} + +template +void textureLinearCommit8(S sampler, ivec2_scalar i, int zoffset, + uint32_t* buf) { + assert(sampler->format == TextureFormat::RGBA8); + ivec2_scalar frac = i & 0x7F; + i >>= 7; + + uint32_t* row0 = + &sampler + ->buf[clampCoord(i.x, sampler->width) + + clampCoord(i.y, sampler->height) * sampler->stride + zoffset]; + uint32_t* row1 = + row0 + + ((i.y > 0 && i.y < int32_t(sampler->height) - 1) ? sampler->stride : 0); + int16_t fracx = i.x > 0 && i.x < int32_t(sampler->width) - 1 ? frac.x : 0; + int16_t fracy = frac.y; + + U32 pix0 = unaligned_load(row0); + U32 pix0n = unaligned_load(row0 + 4); + uint32_t pix0x = row0[8]; + U32 pix1 = unaligned_load(row1); + U32 pix1n = unaligned_load(row1 + 4); + uint32_t pix1x = row1[8]; + + { + auto ab0 = CONVERT(bit_cast>(SHUFFLE(pix0, pix0, 0, 1, 1, 2)), + V16); + auto ab1 = CONVERT(bit_cast>(SHUFFLE(pix1, pix1, 0, 1, 1, 2)), + V16); + ab0 += ((ab1 - ab0) * fracy) >> 7; + + auto cd0 = CONVERT(bit_cast>(SHUFFLE(pix0, pix0n, 2, 3, 3, 4)), + V16); + auto cd1 = CONVERT(bit_cast>(SHUFFLE(pix1, pix1n, 2, 3, 3, 4)), + V16); + cd0 += ((cd1 - cd0) * fracy) >> 7; + + auto abcdl = combine(lowHalf(ab0), lowHalf(cd0)); + auto abcdh = combine(highHalf(ab0), highHalf(cd0)); + abcdl += ((abcdh - abcdl) * fracx) >> 7; + + commit_span(buf, pack(WideRGBA8(abcdl))); + } + + { + auto ab0 = + CONVERT(bit_cast>(SHUFFLE(pix0n, pix0n, 0, 1, 1, 2)), + V16); + auto ab1 = + CONVERT(bit_cast>(SHUFFLE(pix1n, pix1n, 0, 1, 1, 2)), + V16); + ab0 += ((ab1 - ab0) * fracy) >> 7; + + auto cd0 = + CONVERT(bit_cast>(SHUFFLE(pix0n, U32(pix0x), 2, 3, 3, 4)), + V16); + auto cd1 = + CONVERT(bit_cast>(SHUFFLE(pix1n, U32(pix1x), 2, 3, 3, 4)), + V16); + cd0 += ((cd1 - cd0) * fracy) >> 7; + + auto abcdl = combine(lowHalf(ab0), lowHalf(cd0)); + auto abcdh = combine(highHalf(ab0), highHalf(cd0)); + abcdl += ((abcdh - abcdl) * fracx) >> 7; + + commit_span(buf + 4, pack(WideRGBA8(abcdl))); + } +} diff --git a/swgl/src/vector_type.h b/swgl/src/vector_type.h new file mode 100644 index 0000000000..da291cc587 --- /dev/null +++ b/swgl/src/vector_type.h @@ -0,0 +1,473 @@ +/* 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/. */ + +#ifdef __clang__ +# ifdef __SSE2__ +# include +# define USE_SSE2 1 +# endif +# ifdef __ARM_NEON +# include +# define USE_NEON 1 +# endif +#endif + +namespace glsl { + +#ifdef __clang__ +template +using VectorType = T __attribute__((ext_vector_type(N))); + +# define CONVERT(vector, type) __builtin_convertvector(vector, type) +# define SHUFFLE(a, b, ...) __builtin_shufflevector(a, b, __VA_ARGS__) + +template +SI VectorType combine(VectorType a, VectorType b) { + return __builtin_shufflevector(a, b, 0, 1, 2, 3); +} + +template +SI VectorType combine(VectorType a, VectorType b) { + return __builtin_shufflevector(a, b, 0, 1, 2, 3, 4, 5, 6, 7); +} + +template +SI VectorType combine(VectorType a, VectorType b) { + return __builtin_shufflevector(a, b, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15); +} + +template +SI VectorType lowHalf(VectorType a) { + return __builtin_shufflevector(a, a, 0, 1, 2, 3); +} + +template +SI VectorType highHalf(VectorType a) { + return __builtin_shufflevector(a, a, 4, 5, 6, 7); +} + +template +SI VectorType lowHalf(VectorType a) { + return __builtin_shufflevector(a, a, 0, 1, 2, 3, 4, 5, 6, 7); +} + +template +SI VectorType highHalf(VectorType a) { + return __builtin_shufflevector(a, a, 8, 9, 10, 11, 12, 13, 14, 15); +} + +template +SI VectorType expand(VectorType a) { + return __builtin_shufflevector(a, a, 0, 1, 2, 3, -1, -1, -1, -1); +} +#else +template +struct VectorMask { + typedef T type; +}; +template <> +struct VectorMask { + typedef int32_t type; +}; +template <> +struct VectorMask { + typedef int16_t type; +}; +template <> +struct VectorMask { + typedef int8_t type; +}; +template <> +struct VectorMask { + typedef int type; +}; + +template +struct VectorType { + enum { SIZE = N }; + + typedef T data_type __attribute__((vector_size(sizeof(T) * N))); + typedef typename VectorMask::type mask_index; + typedef mask_index mask_type + __attribute__((vector_size(sizeof(mask_index) * N))); + typedef T half_type __attribute__((vector_size(sizeof(T) * (N / 2)))); + union { + data_type data; + struct { + T x, y, z, w; + }; + T elements[N]; + struct { + half_type low_half, high_half; + }; + }; + + VectorType() = default; + constexpr VectorType(T n) : data{n, n, n, n} {} + constexpr VectorType(T a, T b, T c, T d) : data{a, b, c, d} {} + constexpr VectorType(T a, T b, T c, T d, T e, T f, T g, T h) + : data{a, b, c, d, e, f, g, h} {} + constexpr VectorType(T a, T b, T c, T d, T e, T f, T g, T h, T i, T j, T k, + T l, T m, T n, T o, T p) + : data{a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p} {} + + SI VectorType wrap(data_type data) { + VectorType v; + v.data = data; + return v; + } + + T& operator[](size_t i) { return elements[i]; } + T operator[](size_t i) const { return elements[i]; } + + template + operator VectorType() const { + return VectorType::wrap( + (typename VectorType::data_type){U(x), U(y)}); + } + template + operator VectorType() const { + return VectorType::wrap( + (typename VectorType::data_type){U(x), U(y), U(z), U(w)}); + } + template + operator VectorType() const { + return VectorType::wrap((typename VectorType::data_type){ + U(elements[0]), U(elements[1]), U(elements[2]), U(elements[3]), + U(elements[4]), U(elements[5]), U(elements[6]), U(elements[7])}); + } + template + operator VectorType() const { + return VectorType::wrap((typename VectorType::data_type){ + U(elements[0]), + U(elements[1]), + U(elements[2]), + U(elements[3]), + U(elements[4]), + U(elements[5]), + U(elements[6]), + U(elements[7]), + U(elements[8]), + U(elements[9]), + U(elements[10]), + U(elements[11]), + U(elements[12]), + U(elements[13]), + U(elements[14]), + U(elements[15]), + }); + } + + VectorType operator-() const { return wrap(-data); } + VectorType operator~() const { return wrap(~data); } + + VectorType operator&(VectorType x) const { return wrap(data & x.data); } + VectorType operator&(T x) const { return wrap(data & x); } + VectorType operator|(VectorType x) const { return wrap(data | x.data); } + VectorType operator|(T x) const { return wrap(data | x); } + VectorType operator^(VectorType x) const { return wrap(data ^ x.data); } + VectorType operator^(T x) const { return wrap(data ^ x); } + VectorType operator<<(int x) const { return wrap(data << x); } + VectorType operator>>(int x) const { return wrap(data >> x); } + VectorType operator+(VectorType x) const { return wrap(data + x.data); } + VectorType operator+(T x) const { return wrap(data + x); } + friend VectorType operator+(T x, VectorType y) { return wrap(x + y.data); } + VectorType operator-(VectorType x) const { return wrap(data - x.data); } + VectorType operator-(T x) const { return wrap(data - x); } + friend VectorType operator-(T x, VectorType y) { return wrap(x - y.data); } + VectorType operator*(VectorType x) const { return wrap(data * x.data); } + VectorType operator*(T x) const { return wrap(data * x); } + friend VectorType operator*(T x, VectorType y) { return wrap(x * y.data); } + VectorType operator/(VectorType x) const { return wrap(data / x.data); } + VectorType operator/(T x) const { return wrap(data / x); } + friend VectorType operator/(T x, VectorType y) { return wrap(x / y.data); } + VectorType operator%(int x) const { return wrap(data % x); } + + VectorType& operator&=(VectorType x) { + data &= x.data; + return *this; + } + VectorType& operator|=(VectorType x) { + data |= x.data; + return *this; + } + VectorType& operator^=(VectorType x) { + data ^= x.data; + return *this; + } + VectorType& operator<<=(int x) { + data <<= x; + return *this; + } + VectorType& operator>>=(int x) { + data >>= x; + return *this; + } + VectorType& operator+=(VectorType x) { + data += x.data; + return *this; + } + VectorType& operator-=(VectorType x) { + data -= x.data; + return *this; + } + VectorType& operator*=(VectorType x) { + data *= x.data; + return *this; + } + VectorType& operator/=(VectorType x) { + data /= x.data; + return *this; + } + VectorType& operator%=(int x) { + data %= x; + return *this; + } + + VectorType operator==(VectorType x) const { + return VectorType::wrap(data == x.data); + } + VectorType operator!=(VectorType x) const { + return VectorType::wrap(data != x.data); + } + VectorType operator<(VectorType x) const { + return VectorType::wrap(data < x.data); + } + VectorType operator>(VectorType x) const { + return VectorType::wrap(data > x.data); + } + VectorType operator<=(VectorType x) const { + return VectorType::wrap(data <= x.data); + } + VectorType operator>=(VectorType x) const { + return VectorType::wrap(data >= x.data); + } + + VectorType operator!() const { return wrap(!data); } + VectorType operator&&(VectorType x) const { return wrap(data & x.data); } + VectorType operator||(VectorType x) const { return wrap(data | x.data); } + + VectorType& operator=(VectorType x) { + data = x.data; + return *this; + } + + VectorType shuffle(VectorType b, mask_index x, mask_index y, + mask_index z, mask_index w) const { + return VectorType::wrap(__builtin_shuffle( + data, b.data, (typename VectorType::mask_type){x, y, z, w})); + } + VectorType shuffle(VectorType b, mask_index x, mask_index y, + mask_index z, mask_index w, mask_index s, + mask_index t, mask_index u, mask_index v) const { + return VectorType::wrap(__builtin_shuffle( + data, b.data, + (typename VectorType::mask_type){x, y, z, w, s, t, u, v})); + } + VectorType shuffle(VectorType b, mask_index x, mask_index y, + mask_index z, mask_index w, mask_index s, + mask_index t, mask_index u, mask_index v, + mask_index i, mask_index j, mask_index k, + mask_index l, mask_index m, mask_index n, + mask_index o, mask_index p) const { + return VectorType::wrap( + __builtin_shuffle(data, b.data, + (typename VectorType::mask_type){ + x, y, z, w, s, t, u, v, i, j, k, l, m, n, o, p})); + } + + VectorType swizzle(mask_index x, mask_index y, mask_index z, + mask_index w) const { + return VectorType::wrap(__builtin_shuffle( + data, (typename VectorType::mask_type){x, y, z, w})); + } + VectorType swizzle(mask_index x, mask_index y, mask_index z, + mask_index w, mask_index s, mask_index t, + mask_index u, mask_index v) const { + return VectorType::wrap(__builtin_shuffle( + data, (typename VectorType::mask_type){x, y, z, w, s, t, u, v})); + } + + SI VectorType wrap(half_type low, half_type high) { + VectorType v; + v.low_half = low; + v.high_half = high; + return v; + } + + VectorType combine(VectorType high) const { + return VectorType::wrap(data, high.data); + } + +# define xyxy swizzle(0, 1, 0, 1) +# define zwzw swizzle(2, 3, 2, 3) +# define zyxw swizzle(2, 1, 0, 3) +# define xxxxyyyy XXXXYYYY() + VectorType XXXXYYYY() const { + return swizzle(0, 0, 0, 0).combine(swizzle(1, 1, 1, 1)); + } +# define zzzzwwww ZZZZWWWW() + VectorType ZZZZWWWW() const { + return swizzle(2, 2, 2, 2).combine(swizzle(3, 3, 3, 3)); + } +# define xyzwxyzw XYZWXYZW() + VectorType XYZWXYZW() const { return combine(*this); } +# define xyxyxyxy XYXYXYXY() + VectorType XYXYXYXY() const { + return swizzle(0, 1, 0, 1).combine(swizzle(0, 1, 0, 1)); + } +# define zwzwzwzw ZWZWZWZW() + VectorType ZWZWZWZW() const { + return swizzle(2, 3, 2, 3).combine(swizzle(2, 3, 2, 3)); + } +# define xxyyzzww XXYYZZWW() + VectorType XXYYZZWW() const { + return swizzle(0, 0, 1, 1).combine(swizzle(2, 2, 3, 3)); + } +}; + +template +struct VectorType { + typedef T data_type __attribute__((vector_size(sizeof(T) * 2))); + union { + data_type data; + struct { + T x, y; + }; + T elements[2]; + }; +}; + +# define CONVERT(vector, type) ((type)(vector)) +# define SHUFFLE(a, b, ...) a.shuffle(b, __VA_ARGS__) + +template +SI VectorType combine(VectorType a, VectorType b) { + return VectorType::wrap(a.data, b.data); +} + +template +SI VectorType lowHalf(VectorType a) { + return VectorType::wrap(a.low_half); +} + +template +SI VectorType highHalf(VectorType a) { + return VectorType::wrap(a.high_half); +} + +template +SI VectorType expand(VectorType a) { + return combine(a, a); +} +#endif + +template +SI VectorType zipLow(VectorType a, VectorType b) { + return SHUFFLE(a, b, 0, 4, 1, 5); +} + +template +SI VectorType zipHigh(VectorType a, VectorType b) { + return SHUFFLE(a, b, 2, 6, 3, 7); +} + +template +SI VectorType zipLow(VectorType a, VectorType b) { + return SHUFFLE(a, b, 0, 8, 1, 9, 2, 10, 3, 11); +} + +template +SI VectorType zipHigh(VectorType a, VectorType b) { + return SHUFFLE(a, b, 4, 12, 5, 13, 6, 14, 7, 15); +} + +template +SI VectorType zipLow(VectorType a, VectorType b) { + return SHUFFLE(a, b, 0, 1, 2, 3, 4, 5, 6, 7, 16, 17, 18, 19, 20, 21, 22, 23); +} + +template +SI VectorType zipHigh(VectorType a, VectorType b) { + return SHUFFLE(a, b, 8, 9, 10, 11, 12, 13, 14, 15, 24, 25, 26, 27, 28, 29, 30, + 31); +} + +template +SI VectorType zip2Low(VectorType a, VectorType b) { + return SHUFFLE(a, b, 0, 1, 8, 9, 2, 3, 10, 11); +} + +template +SI VectorType zip2High(VectorType a, VectorType b) { + return SHUFFLE(a, b, 4, 5, 12, 13, 6, 7, 14, 15); +} + +template +struct Unaligned { + template + SI T load(const P* p) { + T v; + memcpy(&v, p, sizeof(v)); + return v; + } + + template + SI void store(P* p, T v) { + memcpy(p, &v, sizeof(v)); + } +}; + +#ifndef __clang__ +template +struct Unaligned> { + template + SI VectorType load(const P* p) { + VectorType v; + memcpy(v.elements, p, sizeof(v)); + return v; + } + + template + SI void store(P* p, VectorType v) { + memcpy(p, v.elements, sizeof(v)); + } +}; +#endif + +template +SI T unaligned_load(const P* p) { + return Unaligned::load(p); +} + +template +SI void unaligned_store(P* p, T v) { + Unaligned::store(p, v); +} + +template +SI D bit_cast(const S& src) { + static_assert(sizeof(D) == sizeof(S), ""); + return unaligned_load(&src); +} + +template +using V2 = VectorType; +template +using V4 = VectorType; +using Float = V4; +using I32 = V4; +using I16 = V4; +using U64 = V4; +using U32 = V4; +using U16 = V4; +using U8 = V4; +using Bool = V4; +template +using V8 = VectorType; +template +using V16 = VectorType; + +} // namespace glsl diff --git a/webrender/res/brush_solid.frag.h b/webrender/res/brush_solid.frag.h new file mode 100644 index 0000000000..9f65063816 --- /dev/null +++ b/webrender/res/brush_solid.frag.h @@ -0,0 +1,15 @@ +/* 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/. */ + +ALWAYS_INLINE int draw_span(uint32_t* buf, int len) { + auto color = pack_span(buf, flat_varying_vec4_0); + commit_solid_span(buf, color, len); + return len; +} + +ALWAYS_INLINE int draw_span(uint8_t* buf, int len) { + auto color = pack_span(buf, flat_varying_vec4_0.x); + commit_solid_span(buf, color, len); + return len; +} From b32e67c37320bb0ddd38c9e26d60a963d15649e5 Mon Sep 17 00:00:00 2001 From: Lee Salzman Date: Sat, 14 Mar 2020 21:51:24 +0000 Subject: [PATCH 15/23] Bug 1622520 - only run Renderbuffer cleanup code when erased. r=jrmuizel Differential Revision: https://phabricator.services.mozilla.com/D66868 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/6b4b41968cc165237c76b231d3ce5fcd46dc59ff --- swgl/src/gl.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/swgl/src/gl.cc b/swgl/src/gl.cc index 5c322fd8e4..07d9044f8b 100644 --- a/swgl/src/gl.cc +++ b/swgl/src/gl.cc @@ -151,7 +151,7 @@ struct Framebuffer { struct Renderbuffer { GLuint texture = 0; - ~Renderbuffer(); + void on_erase(); }; TextureFilter gl_filter_to_texture_filter(int type) { @@ -376,8 +376,14 @@ struct ObjectStore { O* find(size_t i) const { return i < size ? objects[i] : nullptr; } + template void on_erase(T* o, ...) {} + template void on_erase(T* o, decltype(&T::on_erase)) { + o->on_erase(); + } + bool erase(size_t i) { if (i < size && objects[i]) { + on_erase(objects[i], nullptr); delete objects[i]; objects[i] = nullptr; if (i < first_free) first_free = i; @@ -1398,7 +1404,7 @@ void GenRenderbuffers(int n, GLuint* result) { } } -Renderbuffer::~Renderbuffer() { +void Renderbuffer::on_erase() { for (auto* fb : ctx->framebuffers) { if (fb) { if (unlink(fb->color_attachment, texture)) { From 377361598009549b4fa770cb9bcf251109acf29a Mon Sep 17 00:00:00 2001 From: Lee Salzman Date: Mon, 16 Mar 2020 09:37:21 +0000 Subject: [PATCH 16/23] Bug 1622668 - exclude swgl from CI test scripts. r=gw Differential Revision: https://phabricator.services.mozilla.com/D66914 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/5f2127c05157c5a264d4729447c06cd8cea8614d --- ci-scripts/linux-debug-tests.sh | 3 ++- ci-scripts/macos-debug-tests.sh | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ci-scripts/linux-debug-tests.sh b/ci-scripts/linux-debug-tests.sh index 46916aeb52..2c2fc756b6 100755 --- a/ci-scripts/linux-debug-tests.sh +++ b/ci-scripts/linux-debug-tests.sh @@ -32,4 +32,5 @@ cargo build ${CARGOFLAGS} popd cargo test ${CARGOFLAGS} \ - --all --exclude compositor-windows --exclude compositor + --all --exclude compositor-windows --exclude compositor \ + --exclude glsl-to-cxx --exclude swgl diff --git a/ci-scripts/macos-debug-tests.sh b/ci-scripts/macos-debug-tests.sh index 609576aec7..2325475d90 100755 --- a/ci-scripts/macos-debug-tests.sh +++ b/ci-scripts/macos-debug-tests.sh @@ -38,4 +38,5 @@ cargo check ${CARGOFLAGS} popd cargo test ${CARGOFLAGS} ${CARGOTESTFLAGS} \ - --all --exclude compositor-windows --exclude compositor + --all --exclude compositor-windows --exclude compositor \ + --exclude glsl-to-cxx --exclude swgl From 7482bd33990f6abcac982ad0744db2735f06bf00 Mon Sep 17 00:00:00 2001 From: Glenn Watson Date: Mon, 16 Mar 2020 09:37:29 +0000 Subject: [PATCH 17/23] Bug 1621390 - Optimize backdrop primitives by collapsing to clear color. r=lsalzman,Bert Differential Revision: https://phabricator.services.mozilla.com/D66362 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/6199f7b91e8bde7b7965585842d7a72e2e406e3e --- webrender/src/batch.rs | 47 ++++++++++++- webrender/src/frame_builder.rs | 13 ++-- webrender/src/picture.rs | 118 ++++++++++++++++++-------------- webrender/src/prim_store/mod.rs | 42 ++++++++++-- 4 files changed, 158 insertions(+), 62 deletions(-) diff --git a/webrender/src/batch.rs b/webrender/src/batch.rs index db81681852..cfa766029e 100644 --- a/webrender/src/batch.rs +++ b/webrender/src/batch.rs @@ -19,7 +19,8 @@ use crate::internal_types::{FastHashMap, SavedTargetIndex, Swizzle, TextureSourc use crate::picture::{Picture3DContext, PictureCompositeMode, PicturePrimitive}; use crate::prim_store::{DeferredResolve, EdgeAaSegmentMask, PrimitiveInstanceKind, PrimitiveVisibilityIndex, PrimitiveVisibilityMask}; use crate::prim_store::{VisibleGradientTile, PrimitiveInstance, PrimitiveOpacity, SegmentInstanceIndex}; -use crate::prim_store::{BrushSegment, ClipMaskKind, ClipTaskIndex, VECS_PER_SEGMENT, SpaceMapper}; +use crate::prim_store::{BrushSegment, ClipMaskKind, ClipTaskIndex, PrimitiveVisibilityFlags}; +use crate::prim_store::{VECS_PER_SEGMENT, SpaceMapper}; use crate::prim_store::image::ImageSource; use crate::render_target::RenderTargetContext; use crate::render_task_graph::{RenderTaskId, RenderTaskGraph}; @@ -193,6 +194,16 @@ impl AlphaBatchList { } } + /// Clear all current batches in this list. This is typically used + /// when a primitive is encountered that occludes all previous + /// content in this batch list. + fn clear(&mut self) { + self.current_batch_index = usize::MAX; + self.current_z_id = ZBufferId::invalid(); + self.batches.clear(); + self.item_rects.clear(); + } + pub fn set_params_and_get_batch( &mut self, key: BatchKey, @@ -290,6 +301,14 @@ impl OpaqueBatchList { } } + /// Clear all current batches in this list. This is typically used + /// when a primitive is encountered that occludes all previous + /// content in this batch list. + fn clear(&mut self) { + self.current_batch_index = usize::MAX; + self.batches.clear(); + } + pub fn set_params_and_get_batch( &mut self, key: BatchKey, @@ -502,6 +521,14 @@ impl AlphaBatchBuilder { } } + /// Clear all current batches in this builder. This is typically used + /// when a primitive is encountered that occludes all previous + /// content in this batch list. + fn clear(&mut self) { + self.alpha_batch_list.clear(); + self.opaque_batch_list.clear(); + } + pub fn build( mut self, batch_containers: &mut Vec, @@ -653,6 +680,14 @@ impl BatchBuilder { } } + /// Clear all current batchers. This is typically used when a primitive + /// is encountered that occludes all previous content in this batch list. + fn clear_batches(&mut self) { + for batcher in &mut self.batchers { + batcher.clear(); + } + } + /// Add a picture to a given batch builder. pub fn add_pic_to_batch( &mut self, @@ -731,6 +766,16 @@ impl BatchBuilder { let prim_info = &ctx.scratch.prim_info[prim_instance.visibility_info.0 as usize]; let bounding_rect = &prim_info.clip_chain.pic_clip_rect; + // If this primitive is a backdrop, that means that it is known to cover + // the entire picture cache background. In that case, the renderer will + // use the backdrop color as a clear color, and so we can drop this + // primitive and any prior primitives from the batch lists for this + // picture cache slice. + if prim_info.flags.contains(PrimitiveVisibilityFlags::IS_BACKDROP) { + self.clear_batches(); + return; + } + let z_id = z_generator.next(); let prim_common_data = &ctx.data_stores.as_common_data(&prim_instance); diff --git a/webrender/src/frame_builder.rs b/webrender/src/frame_builder.rs index 0a272c7550..de399e7682 100644 --- a/webrender/src/frame_builder.rs +++ b/webrender/src/frame_builder.rs @@ -15,7 +15,7 @@ use crate::gpu_types::TransformData; use crate::internal_types::{FastHashMap, PlaneSplitter, SavedTargetIndex}; use crate::picture::{PictureUpdateState, SurfaceInfo, ROOT_SURFACE_INDEX, SurfaceIndex, RecordedDirtyRegion}; use crate::picture::{RetainedTiles, TileCacheInstance, DirtyRegion, SurfaceRenderTasks, SubpixelMode}; -use crate::picture::{TileCacheLogger}; +use crate::picture::{BackdropKind, TileCacheLogger}; use crate::prim_store::{SpaceMapper, PictureIndex, PrimitiveDebugId, PrimitiveScratchBuffer}; use crate::prim_store::{DeferredResolve, PrimitiveVisibilityMask}; use crate::profiler::{FrameProfileCounters, TextureCacheProfileCounters, ResourceProfileCounters}; @@ -883,14 +883,19 @@ pub fn build_render_pass( Some(color) => color.a >= 1.0, None => false, }; - // TODO(gw): Once we have multiple slices enabled, take advantage of - // option to skip clears if the slice is opaque. - let clear_color = if forced_opaque { + let mut clear_color = if forced_opaque { Some(ColorF::WHITE) } else { Some(ColorF::TRANSPARENT) }; + // If this picture cache has a valid color backdrop, we will use + // that as the clear color, skipping the draw of the backdrop + // primitive (and anything prior to it) during batching. + if let Some(BackdropKind::Color { color }) = tile_cache.backdrop.kind { + clear_color = Some(color); + } + // Create an alpha batcher for each of the tasks of this picture. let mut batchers = Vec::new(); for task_id in &task_ids { diff --git a/webrender/src/picture.rs b/webrender/src/picture.rs index 747f011e3f..c13dd59910 100644 --- a/webrender/src/picture.rs +++ b/webrender/src/picture.rs @@ -120,7 +120,7 @@ use crate::prim_store::{SpaceMapper, PrimitiveVisibilityMask, PointKey, Primitiv use crate::prim_store::{SpaceSnapper, PictureIndex, PrimitiveInstance, PrimitiveInstanceKind}; use crate::prim_store::{get_raster_rects, PrimitiveScratchBuffer, RectangleKey}; use crate::prim_store::{OpacityBindingStorage, ImageInstanceStorage, OpacityBindingIndex}; -use crate::prim_store::{ColorBindingStorage, ColorBindingIndex}; +use crate::prim_store::{ColorBindingStorage, ColorBindingIndex, PrimitiveVisibilityFlags}; use crate::print_tree::{PrintTree, PrintTreePrinter}; use crate::render_backend::DataStores; use crate::render_task_graph::RenderTaskId; @@ -1252,7 +1252,7 @@ impl Tile { let clipped_rect = self.current_descriptor.local_valid_rect .intersection(&ctx.local_clip_rect) .unwrap_or_else(PictureRect::zero); - let mut is_opaque = ctx.backdrop.rect.contains_rect(&clipped_rect); + let mut is_opaque = ctx.backdrop.opaque_rect.contains_rect(&clipped_rect); if self.has_compositor_surface { // If we found primitive(s) that are ordered _after_ the first compositor @@ -1338,7 +1338,7 @@ impl Tile { // color tiles. We can definitely support this in DC, so this // should be added as a follow up. let is_simple_prim = - ctx.backdrop.kind.can_be_promoted_to_compositor_surface() && + ctx.backdrop.kind.is_some() && self.current_descriptor.prims.len() == 1 && self.is_opaque && supports_simple_prims; @@ -1349,15 +1349,15 @@ impl Tile { // surface unconditionally (this will drop any previously used // texture cache backing surface). match ctx.backdrop.kind { - BackdropKind::Color { color } => { + Some(BackdropKind::Color { color }) => { TileSurface::Color { color, } } - BackdropKind::Clear => { + Some(BackdropKind::Clear) => { TileSurface::Clear } - BackdropKind::Image => { + None => { // This should be prevented by the is_simple_prim check above. unreachable!(); } @@ -1807,42 +1807,29 @@ impl ::std::fmt::Debug for RecordedDirtyRegion { } #[derive(Debug, Copy, Clone)] -enum BackdropKind { +pub enum BackdropKind { Color { color: ColorF, }, Clear, - Image, -} - -impl BackdropKind { - /// Returns true if the compositor can directly draw this backdrop. - fn can_be_promoted_to_compositor_surface(&self) -> bool { - match self { - BackdropKind::Color { .. } | BackdropKind::Clear => true, - BackdropKind::Image => false, - } - } } /// Stores information about the calculated opaque backdrop of this slice. #[derive(Debug, Copy, Clone)] -struct BackdropInfo { +pub struct BackdropInfo { /// The picture space rectangle that is known to be opaque. This is used /// to determine where subpixel AA can be used, and where alpha blending /// can be disabled. - rect: PictureRect, + pub opaque_rect: PictureRect, /// Kind of the backdrop - kind: BackdropKind, + pub kind: Option, } impl BackdropInfo { fn empty() -> Self { BackdropInfo { - rect: PictureRect::zero(), - kind: BackdropKind::Color { - color: ColorF::BLACK, - }, + opaque_rect: PictureRect::zero(), + kind: None, } } } @@ -2215,7 +2202,7 @@ pub struct TileCacheInstance { /// fine to clear the tiles to this and allow subpixel text on the first slice. pub background_color: Option, /// Information about the calculated backdrop content of this cache. - backdrop: BackdropInfo, + pub backdrop: BackdropInfo, /// The allowed subpixel mode for this surface, which depends on the detected /// opacity of the background. pub subpixel_mode: SubpixelMode, @@ -2830,7 +2817,7 @@ impl TileCacheInstance { image_instances: &ImageInstanceStorage, surface_stack: &[SurfaceIndex], composite_state: &mut CompositeState, - ) -> bool { + ) -> Option { // This primitive exists on the last element on the current surface stack. let prim_surface_index = *surface_stack.last().unwrap(); @@ -2838,7 +2825,7 @@ impl TileCacheInstance { // is no need to add it to any primitive dependencies. let prim_clip_chain = match prim_clip_chain { Some(prim_clip_chain) => prim_clip_chain, - None => return false, + None => return None, }; self.map_local_to_surface.set_target_spatial_node( @@ -2849,12 +2836,12 @@ impl TileCacheInstance { // Map the primitive local rect into picture space. let prim_rect = match self.map_local_to_surface.map(&local_prim_rect) { Some(rect) => rect, - None => return false, + None => return None, }; // If the rect is invalid, no need to create dependencies. if prim_rect.size.is_empty_or_negative() { - return false; + return None; } // If the primitive is directly drawn onto this picture cache surface, then @@ -2894,7 +2881,7 @@ impl TileCacheInstance { rect.inflate(surface.inflation_factor, surface.inflation_factor) } None => { - return false; + return None; } }; @@ -2910,7 +2897,7 @@ impl TileCacheInstance { // If the primitive is outside the tiling rects, it's known to not // be visible. if p0.x == p1.x || p0.y == p1.y { - return false; + return None; } // Build the list of resources that this primitive has dependencies on. @@ -2975,7 +2962,10 @@ impl TileCacheInstance { _ => unreachable!(), }; if color.a >= 1.0 { - backdrop_candidate = Some(BackdropKind::Color { color }); + backdrop_candidate = Some(BackdropInfo { + opaque_rect: pic_clip_rect, + kind: Some(BackdropKind::Color { color }), + }); } } else { let opacity_binding = &opacity_binding_store[opacity_binding_index]; @@ -2997,9 +2987,17 @@ impl TileCacheInstance { if opacity_binding_index == OpacityBindingIndex::INVALID { if let Some(image_properties) = resource_cache.get_image_properties(image_data.key) { - // If this image is opaque, it can be considered as a possible opaque backdrop - if image_properties.descriptor.is_opaque() { - backdrop_candidate = Some(BackdropKind::Image); + // For an image to be a possible opaque backdrop, it must: + // - Have a valid, opaque image descriptor + // - Not use tiling (since they can fail to draw) + // - Not having any spacing / padding + if image_properties.descriptor.is_opaque() && + image_properties.tiling.is_none() && + image_data.tile_spacing == LayoutSize::zero() { + backdrop_candidate = Some(BackdropInfo { + opaque_rect: pic_clip_rect, + kind: None, + }); } } } else { @@ -3209,7 +3207,7 @@ impl TileCacheInstance { PrimitiveInstanceKind::PushClipChain | PrimitiveInstanceKind::PopClipChain => { // Early exit to ensure this doesn't get added as a dependency on the tile. - return false; + return None; } PrimitiveInstanceKind::TextRun { data_handle, .. } => { // Only do these checks if we haven't already disabled subpx @@ -3229,13 +3227,16 @@ impl TileCacheInstance { // correctly determined as we recurse through pictures in take_context. if on_picture_surface && subpx_requested - && !self.backdrop.rect.contains_rect(&pic_clip_rect) { + && !self.backdrop.opaque_rect.contains_rect(&pic_clip_rect) { self.subpixel_mode = SubpixelMode::Deny; } } } PrimitiveInstanceKind::Clear { .. } => { - backdrop_candidate = Some(BackdropKind::Clear); + backdrop_candidate = Some(BackdropInfo { + opaque_rect: pic_clip_rect, + kind: Some(BackdropKind::Clear), + }); } PrimitiveInstanceKind::LineDecoration { .. } | PrimitiveInstanceKind::NormalBorder { .. } | @@ -3249,16 +3250,18 @@ impl TileCacheInstance { // If this primitive considers itself a backdrop candidate, apply further // checks to see if it matches all conditions to be a backdrop. + let mut vis_flags = PrimitiveVisibilityFlags::empty(); + if let Some(backdrop_candidate) = backdrop_candidate { - let is_suitable_backdrop = match backdrop_candidate { - BackdropKind::Clear => { + let is_suitable_backdrop = match backdrop_candidate.kind { + Some(BackdropKind::Clear) => { // Clear prims are special - they always end up in their own slice, // and always set the backdrop. In future, we hope to completely // remove clear prims, since they don't integrate with the compositing // system cleanly. true } - BackdropKind::Image | BackdropKind::Color { .. } => { + Some(BackdropKind::Color { .. }) | None => { // Check a number of conditions to see if we can consider this // primitive as an opaque backdrop rect. Several of these are conservative // checks and could be relaxed in future. However, these checks @@ -3281,12 +3284,25 @@ impl TileCacheInstance { }; if is_suitable_backdrop - && !prim_clip_chain.needs_mask - && pic_clip_rect.contains_rect(&self.backdrop.rect) { - self.backdrop = BackdropInfo { - rect: pic_clip_rect, - kind: backdrop_candidate, - }; + && self.external_surfaces.is_empty() + && !prim_clip_chain.needs_mask { + + if backdrop_candidate.opaque_rect.contains_rect(&self.backdrop.opaque_rect) { + self.backdrop.opaque_rect = backdrop_candidate.opaque_rect; + } + + if let Some(kind) = backdrop_candidate.kind { + if backdrop_candidate.opaque_rect.contains_rect(&self.local_rect) { + // If we have a color backdrop, mark the visibility flags + // of the primitive so it is skipped during batching (and + // also clears any previous primitives). + if let BackdropKind::Color { .. } = kind { + vis_flags |= PrimitiveVisibilityFlags::IS_BACKDROP; + } + + self.backdrop.kind = Some(kind); + } + } } } @@ -3312,7 +3328,7 @@ impl TileCacheInstance { } } - true + Some(vis_flags) } /// Print debug information about this picture cache to a tree printer. @@ -3380,8 +3396,8 @@ impl TileCacheInstance { // Register the opaque region of this tile cache as an occluder, which // is used later in the frame to occlude other tiles. - if self.backdrop.rect.is_well_formed_and_nonempty() { - let backdrop_rect = self.backdrop.rect + if self.backdrop.opaque_rect.is_well_formed_and_nonempty() { + let backdrop_rect = self.backdrop.opaque_rect .intersection(&self.local_rect) .and_then(|r| { r.intersection(&self.local_clip_rect) diff --git a/webrender/src/prim_store/mod.rs b/webrender/src/prim_store/mod.rs index 72e47fe458..ad1e24e61a 100644 --- a/webrender/src/prim_store/mod.rs +++ b/webrender/src/prim_store/mod.rs @@ -1519,6 +1519,20 @@ impl PrimitiveVisibilityMask { pub const MAX_DIRTY_REGIONS: usize = 8 * mem::size_of::(); } +bitflags! { + /// A set of bitflags that can be set in the visibility information + /// for a primitive instance. This can be used to control how primitives + /// are treated during batching. + // TODO(gw): We should also move `is_compositor_surface` to be part of + // this flags struct. + #[cfg_attr(feature = "capture", derive(Serialize))] + pub struct PrimitiveVisibilityFlags: u16 { + /// Implies that this primitive covers the entire picture cache slice, + /// and can thus be dropped during batching and drawn with clear color. + const IS_BACKDROP = 1; + } +} + /// Information stored for a visible primitive about the visible /// rect and associated clip information. #[cfg_attr(feature = "capture", derive(Serialize))] @@ -1539,6 +1553,10 @@ pub struct PrimitiveVisibility { /// a list of clip task ids (one per segment). pub clip_task_index: ClipTaskIndex, + /// A set of flags that define how this primitive should be handled + /// during batching of visibile primitives. + pub flags: PrimitiveVisibilityFlags, + /// A mask defining which of the dirty regions this primitive is visible in. pub visibility_mask: PrimitiveVisibilityMask, @@ -2095,6 +2113,7 @@ impl PrimitiveStore { clip_task_index: ClipTaskIndex::INVALID, combined_local_clip_rect: LayoutRect::zero(), visibility_mask: PrimitiveVisibilityMask::empty(), + flags: PrimitiveVisibilityFlags::empty(), } ); @@ -2156,11 +2175,16 @@ impl PrimitiveStore { prim_instance.is_chased(), ); + // Primitive visibility flags default to empty, but may be supplied + // by the `update_prim_dependencies` method below when picture caching + // is active. + let mut vis_flags = PrimitiveVisibilityFlags::empty(); + if let Some(ref mut tile_cache) = frame_state.tile_cache { // TODO(gw): Refactor how tile_cache is stored in frame_state // so that we can pass frame_state directly to // update_prim_dependencies, rather than splitting borrows. - if !tile_cache.update_prim_dependencies( + match tile_cache.update_prim_dependencies( prim_instance, cluster.spatial_node_index, clip_chain.as_ref(), @@ -2176,11 +2200,16 @@ impl PrimitiveStore { &frame_state.surface_stack, &mut frame_state.composite_state, ) { - prim_instance.visibility_info = PrimitiveVisibilityIndex::INVALID; - // Ensure the primitive clip is popped - perhaps we can use - // some kind of scope to do this automatically in future. - frame_state.clip_chain_stack.pop_clip(); - continue; + Some(flags) => { + vis_flags = flags; + } + None => { + prim_instance.visibility_info = PrimitiveVisibilityIndex::INVALID; + // Ensure the primitive clip is popped - perhaps we can use + // some kind of scope to do this automatically in future. + frame_state.clip_chain_stack.pop_clip(); + continue; + } } } @@ -2308,6 +2337,7 @@ impl PrimitiveStore { clip_task_index: ClipTaskIndex::INVALID, combined_local_clip_rect, visibility_mask: PrimitiveVisibilityMask::empty(), + flags: vis_flags, } ); From 19db2a42f1a25db41610a55e7d2365bcef556b11 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Tue, 17 Mar 2020 05:21:12 +0000 Subject: [PATCH 18/23] Bug 1622256 - Replace use of description() with to_string(). r=kvark,jrmuizel Depends on D66767 Differential Revision: https://phabricator.services.mozilla.com/D66768 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/a0835622f3db257ec911be2b22d0c4a6974c5f5e --- webrender_api/src/channel.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/webrender_api/src/channel.rs b/webrender_api/src/channel.rs index 4bf5436af3..2bc4a5f16b 100644 --- a/webrender_api/src/channel.rs +++ b/webrender_api/src/channel.rs @@ -79,8 +79,7 @@ pub struct MsgReceiver { impl MsgReceiver { pub fn recv(&self) -> Result { - use std::error::Error; - self.rx.recv().map_err(|e| io::Error::new(ErrorKind::Other, e.description())) + self.rx.recv().map_err(|e| io::Error::new(ErrorKind::Other, e.to_string())) } pub fn to_mpsc_receiver(self) -> mpsc::Receiver { From 6e3d6feb9c66f63af42e2435540c3c913b670170 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Tue, 17 Mar 2020 05:21:21 +0000 Subject: [PATCH 19/23] Bug 1622256 - Remove unnecessary parentheses producing compiler warning. r=kvark Depends on D66769 Differential Revision: https://phabricator.services.mozilla.com/D66770 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/52e8e93527739f94d39b22d422df82e558038d49 --- webrender/src/shade.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webrender/src/shade.rs b/webrender/src/shade.rs index 9c15b8828c..a56f58c617 100644 --- a/webrender/src/shade.rs +++ b/webrender/src/shade.rs @@ -957,7 +957,7 @@ impl Shaders { } fn get_yuv_shader_index(buffer_kind: ImageBufferKind) -> usize { - (buffer_kind as usize) + buffer_kind as usize } pub fn get_composite_shader( From 537f6819ad4ba8dce36eeecd49500a2e1357588e Mon Sep 17 00:00:00 2001 From: Lee Salzman Date: Tue, 17 Mar 2020 05:21:31 +0000 Subject: [PATCH 20/23] Bug 1622591 - add missing YUV shader variations. r=jrmuizel Differential Revision: https://phabricator.services.mozilla.com/D66885 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/d5280d6879b80da4113e978e5dc7df089969bcb6 --- swgl/build.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/swgl/build.rs b/swgl/build.rs index 9240af4812..4766d952f7 100644 --- a/swgl/build.rs +++ b/swgl/build.rs @@ -118,11 +118,13 @@ const WR_SHADERS: &'static [&'static str] = &[ "brush_yuv_image", "brush_yuv_imageTEXTURE_2D_YUV_NV12", "brush_yuv_imageYUV", + "brush_yuv_imageYUV_ALPHA_PASS", "brush_yuv_imageYUV_INTERLEAVED", "brush_yuv_imageYUV_NV12_ALPHA_PASS", "brush_yuv_imageYUV_NV12", "brush_yuv_imageYUV_PLANAR", "composite", + "compositeYUV", "cs_blurALPHA_TARGET", "cs_blurCOLOR_TARGET", "cs_border_segment", From bbc57cfc46ffec1676fa7337c23ca658d25680ba Mon Sep 17 00:00:00 2001 From: Glenn Watson Date: Tue, 17 Mar 2020 05:21:40 +0000 Subject: [PATCH 21/23] Bug 1622720 - Add support for the tracy frame profiler to webrender. r=nical,jrmuizel This patch removes the old thread_profiler bindings, and adds support for profiling WR with the tracy profiler, which is a much more advanced frame profiler. Since it's very lightweight, and only instruments annotated CPU and GPU zones, it can retain very large profiles, allowing fine grained analysis of thread interactions, CPU spikes etc. Differential Revision: https://phabricator.services.mozilla.com/D66926 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/f8203119cca43d25f2879ebea2b904e4355e59ad --- Cargo.lock | 34 +++++++++++++-------------- servo-tidy.toml | 1 - webrender/Cargo.toml | 4 ++-- webrender/src/glyph_rasterizer/mod.rs | 4 ---- webrender/src/lib.rs | 2 +- webrender/src/renderer.rs | 18 ++++++++++---- webrender/src/scene_builder_thread.rs | 4 ++++ wrench/src/main.rs | 6 ----- 8 files changed, 36 insertions(+), 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6b7719a205..21e269c4af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -784,11 +784,6 @@ name = "khronos_api" version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "lazy_static" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "lazy_static" version = "1.3.0" @@ -885,6 +880,11 @@ name = "memoffset" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "minidl" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "mio" version = "0.6.16" @@ -1609,16 +1609,6 @@ dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "thread_profiler" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "tileview" version = "0.1.0" @@ -1640,6 +1630,14 @@ dependencies = [ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "tracy-rs" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "minidl 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "typenum" version = "1.10.0" @@ -1823,8 +1821,8 @@ dependencies = [ "serde_json 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "svg_fmt 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_profiler 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "tracy-rs 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "webrender_api 0.61.0", "webrender_build 0.0.1", "wr_malloc_size_of 0.0.1", @@ -2132,7 +2130,6 @@ dependencies = [ "checksum jobserver 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum khronos_api 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" -"checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" "checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" "checksum libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "6281b86796ba5e4366000be6e9e18bf35580adf9e63fbe2294aadb587613a319" @@ -2147,6 +2144,7 @@ dependencies = [ "checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39" "checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" "checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" +"checksum minidl 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "647da2489d438eb707e9bcffe0e47fb6f3f355ed288120740fc1e614cfadb9e9" "checksum mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)" = "71646331f2619b1026cc302f87a2b8b648d5c6dd6937846a16cc8ce0f347f432" "checksum mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "46e73a04c2fa6250b8d802134d56d554a9ec2922bf977777c805ea5def61ce40" "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" @@ -2228,8 +2226,8 @@ dependencies = [ "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" "checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" -"checksum thread_profiler 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5920e77802b177479ab5795767fa48e68f61b2f516c2ac0041e2978dd8efe483" "checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b" +"checksum tracy-rs 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e3aa118469b61af5fead107a4882dc4661a05591b6653bbc546c1c8bbc181047" "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" "checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" diff --git a/servo-tidy.toml b/servo-tidy.toml index 84ec80db86..b45489f8d5 100644 --- a/servo-tidy.toml +++ b/servo-tidy.toml @@ -9,7 +9,6 @@ packages = [ "core-graphics", "core-text", "gl_generator", - "lazy_static", "percent-encoding", "rand", "rand_core", diff --git a/webrender/Cargo.toml b/webrender/Cargo.toml index 98d13381b3..cb051db475 100644 --- a/webrender/Cargo.toml +++ b/webrender/Cargo.toml @@ -11,7 +11,7 @@ edition = "2018" [features] default = ["freetype-lib"] freetype-lib = ["freetype/servo-freetype-sys"] -profiler = ["thread_profiler/thread_profiler"] +profiler = ["tracy-rs/enable_profiler"] debugger = ["ws", "serde_json", "serde", "image_loader", "base64"] capture = ["api/serialize", "ron", "serde", "smallvec/serde"] replay = ["api/deserialize", "ron", "serde", "smallvec/serde"] @@ -45,13 +45,13 @@ ron = { optional = true, version = "0.1.7" } serde = { optional = true, version = "1.0", features = ["serde_derive"] } serde_json = { optional = true, version = "1.0" } smallvec = "0.6" -thread_profiler = "0.1.1" time = "0.1" api = { version = "0.61.0", path = "../webrender_api", package = "webrender_api" } webrender_build = { version = "0.0.1", path = "../webrender_build" } malloc_size_of = { version = "0.0.1", path = "../wr_malloc_size_of", package = "wr_malloc_size_of" } ws = { optional = true, version = "0.9" } svg_fmt = "0.4" +tracy-rs = { version = "0.1" } [dev-dependencies] mozangle = "0.3.1" diff --git a/webrender/src/glyph_rasterizer/mod.rs b/webrender/src/glyph_rasterizer/mod.rs index e180ea9290..4b8b595fe3 100644 --- a/webrender/src/glyph_rasterizer/mod.rs +++ b/webrender/src/glyph_rasterizer/mod.rs @@ -1079,15 +1079,11 @@ mod test_glyph_rasterizer { IdNamespace, ColorU}; use api::units::{Au, DevicePoint}; use crate::render_backend::FrameId; - use thread_profiler::register_thread_with_profiler; use std::sync::Arc; use crate::glyph_rasterizer::{FORMAT, FontInstance, BaseFontInstance, GlyphKey, GlyphRasterizer}; let worker = ThreadPoolBuilder::new() .thread_name(|idx|{ format!("WRWorker#{}", idx) }) - .start_handler(move |idx| { - register_thread_with_profiler(format!("WRWorker#{}", idx)); - }) .build(); let workers = Arc::new(worker.unwrap()); let mut glyph_rasterizer = GlyphRasterizer::new(workers).unwrap(); diff --git a/webrender/src/lib.rs b/webrender/src/lib.rs index ca3d5bbb3c..dc11147586 100644 --- a/webrender/src/lib.rs +++ b/webrender/src/lib.rs @@ -70,7 +70,7 @@ extern crate malloc_size_of_derive; #[macro_use] extern crate serde; #[macro_use] -extern crate thread_profiler; +extern crate tracy_rs; extern crate malloc_size_of; extern crate svg_fmt; diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index 0d0c71862e..dc017c0d1c 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -112,7 +112,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{channel, Receiver}; use std::thread; use std::cell::RefCell; -use thread_profiler::{register_thread_with_profiler, write_profile}; +use tracy_rs::register_thread_with_profiler; use time::precise_time_ns; cfg_if! { @@ -2033,6 +2033,16 @@ impl Renderer { start_size: DeviceIntSize, ) -> Result<(Self, RenderApiSender), RendererError> { if !wr_has_been_initialized() { + // If the profiler feature is enabled, try to load the profiler shared library + // if the path was provided. + #[cfg(feature = "profiler")] + unsafe { + if let Ok(ref tracy_path) = std::env::var("WR_TRACY_PATH") { + let ok = tracy_rs::load(tracy_path); + println!("Load tracy from {} -> {}", tracy_path, ok); + } + } + register_thread_with_profiler("Compositor".to_owned()); } @@ -3051,6 +3061,8 @@ impl Renderer { // event. Otherwise they would just pile up in this vector forever. self.notifications.clear(); + tracy_frame_marker!(); + result } @@ -5938,10 +5950,6 @@ impl Renderer { self.debug_flags = flags; } - pub fn save_cpu_profile(&self, filename: &str) { - write_profile(filename); - } - fn draw_frame_debug_items(&mut self, items: &[DebugItem]) { if items.is_empty() { return; diff --git a/webrender/src/scene_builder_thread.rs b/webrender/src/scene_builder_thread.rs index 186e622233..4827c5a6a3 100644 --- a/webrender/src/scene_builder_thread.rs +++ b/webrender/src/scene_builder_thread.rs @@ -320,6 +320,8 @@ impl SceneBuilderThread { } loop { + tracy_begin_frame!("scene_builder_thread"); + match self.rx.recv() { Ok(SceneBuilderRequest::WakeUp) => {} Ok(SceneBuilderRequest::Flush(tx)) => { @@ -379,6 +381,8 @@ impl SceneBuilderThread { if let Some(ref hooks) = self.hooks { hooks.poke(); } + + tracy_end_frame!("scene_builder_thread"); } if let Some(ref hooks) = self.hooks { diff --git a/wrench/src/main.rs b/wrench/src/main.rs index 7a7fe2bc99..4ffd0eddc2 100644 --- a/wrench/src/main.rs +++ b/wrench/src/main.rs @@ -764,7 +764,6 @@ fn render<'a>( let mut show_help = false; let mut do_loop = false; - let mut cpu_profile_index = 0; let mut cursor_position = WorldPoint::zero(); window.update(wrench); @@ -897,11 +896,6 @@ fn render<'a>( show_help = !show_help; do_render = true; } - VirtualKeyCode::T => { - let file_name = format!("profile-{}.json", cpu_profile_index); - wrench.renderer.save_cpu_profile(&file_name); - cpu_profile_index += 1; - } VirtualKeyCode::C => { let path = PathBuf::from("../captures/wrench"); wrench.api.save_capture(path, CaptureBits::all()); From dcac3cc4b8a0a22eeb9f8ae2cd2fefc4377921a9 Mon Sep 17 00:00:00 2001 From: Nicolas Silva Date: Tue, 17 Mar 2020 16:21:04 +0000 Subject: [PATCH 22/23] Bug 1618116 - Remove synchronous hit testing from WebRender's C++ wrapper. r=botond,kats In addition: - Move the fast hit tester to the rust side of the bindings. - Avoid blocking by requesting a hit tester early and only blocking if the request isn't delivered by the time of the first hit test query. Differential Revision: https://phabricator.services.mozilla.com/D66994 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/6096a87c64ab2717f1b0c670589dad69ee219269 --- webrender_api/src/api.rs | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/webrender_api/src/api.rs b/webrender_api/src/api.rs index c2bf8c4faf..ad57931a73 100644 --- a/webrender_api/src/api.rs +++ b/webrender_api/src/api.rs @@ -6,7 +6,7 @@ extern crate serde_bytes; -use crate::channel::{self, MsgSender, Payload, PayloadSender}; +use crate::channel::{self, MsgReceiver, MsgSender, Payload, PayloadSender}; use peek_poke::PeekPoke; use std::cell::Cell; use std::fmt; @@ -1684,13 +1684,14 @@ impl RenderApi { } /// Synchronously request an object that can perform fast hit testing queries. - pub fn request_hit_tester(&self, document_id: DocumentId) -> Arc { + pub fn request_hit_tester(&self, document_id: DocumentId) -> HitTesterRequest { let (tx, rx) = channel::msg_channel().unwrap(); self.send_frame_msg( document_id, FrameMsg::RequestHitTester(tx) ); - rx.recv().unwrap() + + HitTesterRequest { rx } } /// Setup the output region in the framebuffer for a given document. @@ -1781,6 +1782,20 @@ impl Drop for RenderApi { } } +/// A hit tester requested to the render backend thread but not necessarily ready yet. +/// +/// The request should be resolved as late as possible to reduce the likelihood of blocking. +pub struct HitTesterRequest { + rx: MsgReceiver>, +} + +impl HitTesterRequest { + /// Block until the hit tester is available and return it, consuming teh request. + pub fn resolve(self) -> Arc { + self.rx.recv().unwrap() + } +} + /// #[derive(Clone, Deserialize, Serialize)] pub struct ScrollNodeState { From 2f3e55b791f5bbcfd76591eeed4777f3abf45eb7 Mon Sep 17 00:00:00 2001 From: Nicolas Silva Date: Tue, 17 Mar 2020 21:58:15 +0000 Subject: [PATCH 23/23] Bug 1615613 - Integrate the text shader in the brush infrastructure. r=gw As a strating point for the vertex shader, this patch isolates the parts that are common to both shaders: the code that fetches various piece of data, adding a branching point between the text shader and other brushes just after having fetched most of the data. Hopefully we can devise ways to further unify the vertex shaders in followups. Differential Revision: https://phabricator.services.mozilla.com/D63094 [ghsync] From https://hg.mozilla.org/mozilla-central/rev/26255dd68307e91c80f322ff18233ed22739127d --- webrender/res/brush.glsl | 45 +++++++++++++---- webrender/res/brush_multi.glsl | 52 ++++++++++++++++++- webrender/res/ps_text_run.glsl | 92 +++++++++++++++++++++------------- webrender/src/shade.rs | 6 ++- 4 files changed, 147 insertions(+), 48 deletions(-) diff --git a/webrender/res/brush.glsl b/webrender/res/brush.glsl index 410b690ecb..4589e34b29 100644 --- a/webrender/res/brush.glsl +++ b/webrender/res/brush.glsl @@ -92,6 +92,17 @@ FWD_DECLARE_VS_FUNCTION(radial_gradient_brush_vs) FWD_DECLARE_VS_FUNCTION(conic_gradient_brush_vs) FWD_DECLARE_VS_FUNCTION(yuv_brush_vs) FWD_DECLARE_VS_FUNCTION(opacity_brush_vs) +FWD_DECLARE_VS_FUNCTION(text_brush_vs) + +// Forward-declare the text vertex shader entry point which is currently +// different from other brushes. +void text_shader_main( + Instance instance, + PrimitiveHeader ph, + Transform transform, + PictureTask task, + ClipArea clip_area +); void multi_brush_vs( VertexInfo vi, @@ -120,12 +131,15 @@ void multi_brush_vs( #define INVALID_SEGMENT_INDEX 0xffff -void main(void) { - // Load the brush instance from vertex attributes. - Instance instance = decode_instance_attributes(); +void brush_shader_main_vs( + Instance instance, + PrimitiveHeader ph, + Transform transform, + PictureTask pic_task, + ClipArea clip_area +) { int edge_flags = instance.flags & 0xff; int brush_flags = (instance.flags >> 8) & 0xff; - PrimitiveHeader ph = fetch_prim_header(instance.prim_header_address); // Fetch the segment of this brush primitive we are drawing. vec4 segment_data; @@ -156,12 +170,6 @@ void main(void) { VertexInfo vi; - // Fetch the dynamic picture that we are drawing on. - PictureTask pic_task = fetch_picture_task(instance.picture_task_address); - ClipArea clip_area = fetch_clip_area(instance.clip_address); - - Transform transform = fetch_transform(ph.transform_id); - // Write the normal vertex information out. if (transform.is_axis_aligned) { @@ -245,8 +253,25 @@ void main(void) { } +#ifndef WR_VERTEX_SHADER_MAIN_FUNCTION +// If the entry-point was not overridden before including the brush shader, +// use the default one. +#define WR_VERTEX_SHADER_MAIN_FUNCTION brush_shader_main_vs #endif +void main(void) { + + Instance instance = decode_instance_attributes(); + PrimitiveHeader ph = fetch_prim_header(instance.prim_header_address); + Transform transform = fetch_transform(ph.transform_id); + PictureTask task = fetch_picture_task(instance.picture_task_address); + ClipArea clip_area = fetch_clip_area(instance.clip_address); + + WR_VERTEX_SHADER_MAIN_FUNCTION(instance, ph, transform, task, clip_area); +} + +#endif // WR_VERTEX_SHADER + #ifdef WR_FRAGMENT_SHADER // Foward-declare all brush entry-points. diff --git a/webrender/res/brush_multi.glsl b/webrender/res/brush_multi.glsl index 8188c70da6..447186b1fc 100644 --- a/webrender/res/brush_multi.glsl +++ b/webrender/res/brush_multi.glsl @@ -7,7 +7,7 @@ // This type of uber-shader comes at a cost so the goal for this is to // provide opportunities for aggressive batching when the number of draw // calls so high that reducing the number of draw calls is worth the -// cost of this "über-shader". +// cost of this "uber-shader". #define WR_FEATURE_MULTI_BRUSH @@ -26,8 +26,30 @@ int vecs_per_brush(int brush_kind); + +#ifdef WR_FEATURE_TEXT_BRUSH +// Before including the brush source, if we need support for text we override +// the vertex shader's main entry point with one that can call into the text +// shader or the regular brush shaders. + +// Foward-declare the new entry point. +void multi_brush_main_vs( + Instance instance, + PrimitiveHeader ph, + Transform transform, + PictureTask pic_task, + ClipArea clip_area +); + +// Override the default entry point. +#define WR_VERTEX_SHADER_MAIN_FUNCTION multi_brush_main_vs + +#endif + + #include shared,prim_shared,brush + #ifdef WR_FEATURE_IMAGE_BRUSH #include brush_image #endif @@ -88,6 +110,30 @@ int vecs_per_brush(int brush_kind); #include brush_opacity #endif +#undef VECS_PER_SPECIFIC_BRUSH +#undef WR_BRUSH_VS_FUNCTION +#undef WR_BRUSH_FS_FUNCTION + +#ifdef WR_FEATURE_TEXT_BRUSH +#include ps_text_run + +// Special entry point when text support is needed. +void multi_brush_main_vs( + Instance instance, + PrimitiveHeader ph, + Transform transform, + PictureTask pic_task, + ClipArea clip_area +) { + if (instance.brush_kind == BRUSH_SHADER_KIND_TEXT) { + text_shader_main(instance, ph, transform, task, clip_area); + } else { + brush_shader_main(instance, ph, transform, task, clip_area); + } +} + +#endif + int vecs_per_brush(int brush_kind) { switch (brush_kind) { // The default arm should never be taken, we let it point to whichever shader @@ -240,6 +286,10 @@ Fragment multi_brush_fs(int brush_kind) { #ifdef WR_FEATURE_OPACITY_BRUSH case BRUSH_KIND_OPACITY: return opacity_brush_fs(); #endif + + #ifdef WR_FEATURE_TEXT_BRUSH + case BRUSH_KIND_TEXT: return text_brush_fs(); + #endif } } diff --git a/webrender/res/ps_text_run.glsl b/webrender/res/ps_text_run.glsl index 59c23cb7b2..c2f626dded 100644 --- a/webrender/res/ps_text_run.glsl +++ b/webrender/res/ps_text_run.glsl @@ -2,19 +2,28 @@ * 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/. */ +#define WR_VERTEX_SHADER_MAIN_FUNCTION text_shader_main_vs +#define WR_BRUSH_FS_FUNCTION text_brush_fs +#define WR_BRUSH_VS_FUNCTION text_brush_vs +// The text brush shader doesn't use this but the macro must be defined +// to compile the brush infrastructure. +#define VECS_PER_SPECIFIC_BRUSH 0 + #include shared,prim_shared -// A few varying slots for the brushes to use. -// Using these instead of adding dedicated varyings avoids using a high -// number of varyings in the multi-brush shader. -// -// TODO: This is duplicated from the brush shader code and will be merged -// back once text runs use the brush infrastructure. -flat varying vec4 flat_varying_vec4_0; -flat varying vec4 flat_varying_vec4_1; -flat varying vec4 flat_varying_vec4_2; -varying vec4 varying_vec4_0; -varying vec4 varying_vec4_1; +#ifdef WR_VERTEX_SHADER +// Forward-declare the text vertex shader's main entry-point before including +// the brush shader. +void text_shader_main_vs( + Instance instance, + PrimitiveHeader ph, + Transform transform, + PictureTask task, + ClipArea clip_area +); +#endif + +#include brush #define V_COLOR flat_varying_vec4_0 #define V_MASK_SWIZZLE flat_varying_vec4_1.xy @@ -112,18 +121,17 @@ vec2 get_snap_bias(int subpx_dir) { } } -void main(void) { - Instance instance = decode_instance_attributes(); - +void text_shader_main_vs( + Instance instance, + PrimitiveHeader ph, + Transform transform, + PictureTask task, + ClipArea clip_area +) { int glyph_index = instance.segment_index; int subpx_dir = (instance.flags >> 8) & 0xff; int color_mode = instance.flags & 0xff; - PrimitiveHeader ph = fetch_prim_header(instance.prim_header_address); - Transform transform = fetch_transform(ph.transform_id); - ClipArea clip_area = fetch_clip_area(instance.clip_address); - PictureTask task = fetch_picture_task(instance.picture_task_address); - // Note that the reference frame relative offset is stored in the prim local // rect size during batching, instead of the actual size of the primitive. TextRun text = fetch_text_run(ph.specific_prim_address); @@ -272,7 +280,27 @@ void main(void) { V_LAYER = res.layer; V_UV_BOUNDS = (res.uv_rect + vec4(0.5, 0.5, -0.5, -0.5)) / texture_size.xyxy; } -#endif + +void text_brush_vs( + VertexInfo vi, + int prim_address, + RectWithSize prim_rect, + RectWithSize segment_rect, + ivec4 prim_user_data, + int specific_resource_address, + mat4 transform, + PictureTask pic_task, + int brush_flags, + vec4 segment_data +) { + // This function is empty and unused for now. It has to be defined to build the shader + // as a brush, but the brush shader currently branches into text_shader_main_vs earlier + // instead of using the regular brush vertex interface for text. + // In the future we should strive to further unify text and brushes, and actually make + // use of this function. +} + +#endif // WR_VERTEX_SHADER #ifdef WR_FRAGMENT_SHADER @@ -296,20 +324,12 @@ Fragment text_brush_fs(void) { return frag; } -void main(void) { - Fragment frag = text_brush_fs(); - - float clip_mask = do_clip(); - frag.color *= clip_mask; - - #if defined(WR_FEATURE_DEBUG_OVERDRAW) - oFragColor = WR_DEBUG_OVERDRAW_COLOR; - #elif defined(WR_FEATURE_DUAL_SOURCE_BLENDING) - oFragColor = frag.color; - oFragBlend = frag.blend * clip_mask; - #else - write_output(frag.color); - #endif -} - #endif // WR_FRAGMENT_SHADER + +// Undef macro names that could be re-defined by other shaders. +#undef V_COLOR +#undef V_MASK_SWIZZLE +#undef V_UV_BOUNDS +#undef V_UV +#undef V_LAYER +#undef V_UV_CLIP diff --git a/webrender/src/shade.rs b/webrender/src/shade.rs index a56f58c617..585917b14f 100644 --- a/webrender/src/shade.rs +++ b/webrender/src/shade.rs @@ -421,16 +421,20 @@ impl TextShader { features: &[&'static str], precache_flags: ShaderPrecacheFlags, ) -> Result { + let mut simple_features = features.to_vec(); + simple_features.push("ALPHA_PASS"); + let simple = LazilyCompiledShader::new( ShaderKind::Text, name, - features, + &simple_features, device, precache_flags, )?; let mut glyph_transform_features = features.to_vec(); glyph_transform_features.push("GLYPH_TRANSFORM"); + glyph_transform_features.push("ALPHA_PASS"); let glyph_transform = LazilyCompiledShader::new( ShaderKind::Text,