From 6c31e1bc01fe9dbcb533a86979b5717c54bd1325 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Mon, 22 Apr 2019 13:50:38 -0700 Subject: [PATCH] In VR mode, render one eye and then reproject to both eyes instead of rendering twice. This reduces both CPU and GPU time a lot in exchange for a small loss in quality. --- Cargo.lock | 107 +++++- .../PathfinderDemoActivity.java | 10 +- .../PathfinderDemoFileBrowserActivity.java | 1 - .../PathfinderDemoRenderer.java | 2 + demo/common/src/lib.rs | 326 ++++++++++++------ demo/common/src/window.rs | 6 +- demo/native/Cargo.toml | 1 + demo/native/src/main.rs | 18 +- geometry/src/color.rs | 5 + gl/src/lib.rs | 35 +- gpu/src/lib.rs | 21 +- renderer/src/builder.rs | 7 - renderer/src/gpu/renderer.rs | 117 ++++++- renderer/src/scene.rs | 5 - resources/shaders/debug_texture.fs.glsl | 2 +- resources/shaders/post.vs.glsl | 2 +- resources/shaders/reproject.fs.glsl | 26 ++ resources/shaders/reproject.vs.glsl | 24 ++ 18 files changed, 559 insertions(+), 156 deletions(-) create mode 100644 resources/shaders/reproject.fs.glsl create mode 100644 resources/shaders/reproject.vs.glsl diff --git a/Cargo.lock b/Cargo.lock index ad8fc6bc..2f713a2b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -64,6 +64,15 @@ dependencies = [ "image 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "argon2rs" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "arrayvec" version = "0.4.10" @@ -128,6 +137,15 @@ name = "bitflags" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "blake2-rfc" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "block" version = "0.1.6" @@ -198,6 +216,15 @@ dependencies = [ "objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "color-backtrace" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "color_quant" version = "1.0.1" @@ -215,6 +242,11 @@ dependencies = [ "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "constant_time_eq" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "core-foundation" version = "0.6.4" @@ -292,6 +324,7 @@ dependencies = [ name = "demo" version = "0.1.0" dependencies = [ + "color-backtrace 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "gl 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "jemallocator 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "nfd 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -304,6 +337,16 @@ dependencies = [ "sdl2-sys 0.32.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "dirs" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_users 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "dlib" version = "0.4.1" @@ -370,6 +413,26 @@ dependencies = [ "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "failure" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "failure_derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "fixedbitset" version = "0.1.9" @@ -1274,6 +1337,17 @@ dependencies = [ "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "redox_users" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "regex" version = "0.1.80" @@ -1550,6 +1624,27 @@ dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "synstructure" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "term" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "termcolor" version = "1.0.4" @@ -1715,7 +1810,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", - "xml-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1836,6 +1931,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum android_glue 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "000444226fcff248f2bc4c7625be32c63caccfecc2723a2b9f78a7487a49c407" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3" +"checksum argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3f67b0b6a86dae6e67ff4ca2b6201396074996379fba2b92ff649126f37cb392" "checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" "checksum ascii 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a5fc969a8ce2c9c0c4b0429bb8431544f6658283c8326ba5ff8c762b75369335" "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" @@ -1844,6 +1940,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6" "checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" +"checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" "checksum block 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" "checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" "checksum cc 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)" = "4390a3b5f4f6bce9c1d0c00128379df433e53777fdd30e92f16a529332baec4e" @@ -1853,8 +1950,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" "checksum cocoa 0.18.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cf79daa4e11e5def06e55306aa3601b87de6b5149671529318da048f67cdd77b" +"checksum color-backtrace 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "90242aff9b6439332beb77ee416126367adcd6376b0dc80b39250e7debdd913d" "checksum color_quant 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0dbbb57365263e881e805dc77d94697c9118fd94d8da011240555aa7b23445bd" "checksum combine 3.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "d2623b3542b48f4427e15ddd4995186decb594ebbd70271463886584b4a114b9" +"checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e" "checksum core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" "checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" "checksum core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)" = "56790968ab1c8a1202a102e6de05fc6e1ec87da99e4e93e9a7d13efbfc1e95a9" @@ -1863,6 +1962,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150" "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" "checksum deflate 0.7.19 (registry+https://github.com/rust-lang/crates.io-index)" = "8a6abb26e16e8d419b5c78662aa9f82857c2386a073da266840e474d5055ec86" +"checksum dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" "checksum dlib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "77e51249a9d823a4cb79e3eca6dcd756153e8ed0157b6c04775d04bf1b13b76a" "checksum downcast-rs 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f2b92dfd5c2f75260cbf750572f95d387e7ca0ba5e3fbe9e1a33f23025be020f" "checksum egl 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a373bc9844200b1ff15bd1b245931d1c20d09d06e4ec09f361171f29a4b0752d" @@ -1871,6 +1971,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "07e791d3be96241c77c43846b665ef1384606da2cd2a48730abe606a12906e02" "checksum euclid 0.19.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d1a7698bdda3d7444a79d33bdc96e8b518d44ea3ff101d8492a6ca1207b886ea" "checksum euclid_macros 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fdcb84c18ea5037a1c5a23039b4ff29403abce2e0d6b1daa11cf0bde2b30be15" +"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" +"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" "checksum fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33" "checksum float-cmp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "134a8fa843d80a51a5b77d36d42bc2def9edcb0262c914861d08129fd1926600" "checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" @@ -1961,6 +2063,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" "checksum redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)" = "423e376fffca3dfa06c9e9790a9ccd282fafb3cc6e6397d01dbf64f9bacc6b85" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" +"checksum redox_users 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe5204c3a17e97dde73f285d49be585df59ed84b50a872baf416e73b62c3828" "checksum regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f" "checksum regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37e7cbbd370869ce2e8dff25c7018702d10b21a20ef7135316f8daecd6c25b7f" "checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957" @@ -1996,6 +2099,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum svgdom 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a9b53b3ed152fc6b871f7232a8772c640567fd25d056941450637ecba32924d" "checksum svgtypes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c43c25e6de7264024b5e351eb0c342039eb5acf51f2e9d0099bbd324b661453b" "checksum syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)" = "f92e629aa1d9c827b2bb8297046c1ccffc57c99b947a680d3ccff1f136a3bee9" +"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015" +"checksum term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42" "checksum termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4096add70612622289f2fdcdbd5086dc81c1e2675e6ae58d6c4f62a16c6d7f2f" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" "checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6" diff --git a/demo/android/app/src/main/java/graphics/pathfinder/pathfinderdemo/PathfinderDemoActivity.java b/demo/android/app/src/main/java/graphics/pathfinder/pathfinderdemo/PathfinderDemoActivity.java index c7686551..88c7fa59 100644 --- a/demo/android/app/src/main/java/graphics/pathfinder/pathfinderdemo/PathfinderDemoActivity.java +++ b/demo/android/app/src/main/java/graphics/pathfinder/pathfinderdemo/PathfinderDemoActivity.java @@ -18,6 +18,7 @@ import android.support.annotation.RequiresApi; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.os.Bundle; +import android.util.Log; import android.view.MotionEvent; import android.view.View; @@ -61,13 +62,8 @@ public class PathfinderDemoActivity extends Activity { @RequiresApi(api = Build.VERSION_CODES.N) void setVRMode(boolean enabled) { - try { - setVrModeEnabled(false, mVRListenerComponentName); - mContentView.setStereoModeEnabled(enabled); - mContentView.setDistortionCorrectionEnabled(false); - } catch (PackageManager.NameNotFoundException exception) { - startActivity(new Intent(Settings.ACTION_VR_LISTENER_SETTINGS)); - } + mContentView.setStereoModeEnabled(enabled); + mContentView.setDistortionCorrectionEnabled(false); } @RequiresApi(api = Build.VERSION_CODES.N) diff --git a/demo/android/app/src/main/java/graphics/pathfinder/pathfinderdemo/PathfinderDemoFileBrowserActivity.java b/demo/android/app/src/main/java/graphics/pathfinder/pathfinderdemo/PathfinderDemoFileBrowserActivity.java index 415d648f..7551c0f9 100644 --- a/demo/android/app/src/main/java/graphics/pathfinder/pathfinderdemo/PathfinderDemoFileBrowserActivity.java +++ b/demo/android/app/src/main/java/graphics/pathfinder/pathfinderdemo/PathfinderDemoFileBrowserActivity.java @@ -44,5 +44,4 @@ public class PathfinderDemoFileBrowserActivity extends Activity { } }); } - } diff --git a/demo/android/app/src/main/java/graphics/pathfinder/pathfinderdemo/PathfinderDemoRenderer.java b/demo/android/app/src/main/java/graphics/pathfinder/pathfinderdemo/PathfinderDemoRenderer.java index 2662f04b..e9035dd2 100644 --- a/demo/android/app/src/main/java/graphics/pathfinder/pathfinderdemo/PathfinderDemoRenderer.java +++ b/demo/android/app/src/main/java/graphics/pathfinder/pathfinderdemo/PathfinderDemoRenderer.java @@ -2,6 +2,7 @@ package graphics.pathfinder.pathfinderdemo; import android.content.Intent; import android.content.pm.PackageManager; +import android.util.Log; import com.google.vr.sdk.base.Eye; import com.google.vr.sdk.base.GvrView; @@ -52,6 +53,7 @@ public class PathfinderDemoRenderer extends Object implements GvrView.Renderer { mInVRMode = inVR; try { mActivity.setVrModeEnabled(mInVRMode, mActivity.mVRListenerComponentName); + mActivity.setVRMode(inVR); } catch (PackageManager.NameNotFoundException exception) { throw new RuntimeException(exception); } diff --git a/demo/common/src/lib.rs b/demo/common/src/lib.rs index 67cdbe9b..4aa5d165 100644 --- a/demo/common/src/lib.rs +++ b/demo/common/src/lib.rs @@ -12,19 +12,18 @@ use crate::device::{GroundLineVertexArray, GroundProgram, GroundSolidVertexArray}; use crate::ui::{DemoUI, UIAction}; -use crate::window::{CameraTransform, Event, Keycode, SVGPath, View, Window, WindowSize}; +use crate::window::{Event, Keycode, OcularTransform, SVGPath, View, Window, WindowSize}; use clap::{App, Arg}; use image::ColorType; use pathfinder_geometry::basic::point::{Point2DF32, Point2DI32, Point3DF32}; use pathfinder_geometry::basic::rect::RectF32; use pathfinder_geometry::basic::transform2d::Transform2DF32; use pathfinder_geometry::basic::transform3d::{Perspective, Transform3DF32}; -use pathfinder_geometry::color::ColorU; -use pathfinder_geometry::distortion::BarrelDistortionCoefficients; +use pathfinder_geometry::color::{ColorF, ColorU}; use pathfinder_gl::GLDevice; use pathfinder_gpu::resources::ResourceLoader; -use pathfinder_gpu::{DepthFunc, DepthState, Device, Primitive, RenderState, StencilFunc}; -use pathfinder_gpu::{StencilState, UniformData}; +use pathfinder_gpu::{ClearParams, DepthFunc, DepthState, Device, Primitive, RenderState}; +use pathfinder_gpu::{StencilFunc, StencilState, TextureFormat, UniformData}; use pathfinder_renderer::builder::{RenderOptions, RenderTransform, SceneBuilder}; use pathfinder_renderer::gpu::renderer::{DestFramebuffer, RenderMode, RenderStats, Renderer}; use pathfinder_renderer::gpu_data::RenderCommand; @@ -37,7 +36,6 @@ use std::f32::consts::FRAC_PI_4; use std::fmt::{Debug, Formatter, Result as DebugResult}; use std::fs::File; use std::io::Read; -use std::iter; use std::panic::{self, AssertUnwindSafe}; use std::path::PathBuf; use std::process; @@ -72,6 +70,9 @@ const MESSAGE_TIMEOUT_SECS: u64 = 5; const MAX_MESSAGES_IN_FLIGHT: usize = 256; +// Half of the eye separation distance. +const DEFAULT_EYE_OFFSET: f32 = 0.025; + pub const GRIDLINE_COUNT: u8 = 10; pub mod window; @@ -104,6 +105,8 @@ pub struct DemoApp where W: Window { scene_thread_proxy: SceneThreadProxy, renderer: Renderer, + scene_framebuffer: Option<::Framebuffer>, + ground_program: GroundProgram, ground_solid_vertex_array: GroundSolidVertexArray, ground_line_vertex_array: GroundLineVertexArray, @@ -178,6 +181,8 @@ impl DemoApp where W: Window { scene_thread_proxy, renderer, + scene_framebuffer: None, + ground_program, ground_solid_vertex_array, ground_line_vertex_array, @@ -195,61 +200,96 @@ impl DemoApp where W: Window { self.build_scene(); // Get the render message, and determine how many scenes it contains. - let transforms = match self.scene_thread_proxy.receiver.recv().unwrap() { - SceneToMainMsg::BeginFrame { transforms } => transforms, + let transform = match self.scene_thread_proxy.receiver.recv().unwrap() { + SceneToMainMsg::BeginFrame { transform } => transform, msg => panic!("Expected BeginFrame message, found {:?}!", msg), }; - let render_scene_count = transforms.len() as u32; // Save the frame. - self.current_frame = Some(Frame::new(transforms, ui_events)); + self.current_frame = Some(Frame::new(transform, ui_events)); + + // Initialize and set the appropriate framebuffer. + let view = self.ui.mode.view(0); + self.window.make_current(view); + let window_size = self.window_size.device_size(); + let scene_count = match self.camera.mode() { + Mode::VR => { + let viewport = self.window.viewport(View::Stereo(0)); + if self.scene_framebuffer.is_none() || + self.renderer + .device + .texture_size(&self.renderer + .device + .framebuffer_texture(self.scene_framebuffer + .as_ref() + .unwrap())) != + viewport.size() { + let scene_texture = self.renderer.device.create_texture(TextureFormat::RGBA8, + viewport.size()); + self.scene_framebuffer = + Some(self.renderer.device.create_framebuffer(scene_texture)); + } + self.renderer + .replace_dest_framebuffer(DestFramebuffer::Other(self.scene_framebuffer + .take() + .unwrap())); + 2 + } + _ => { + self.renderer.replace_dest_framebuffer(DestFramebuffer::Default { + viewport: self.window.viewport(View::Mono), + window_size, + }); + 1 + } + }; // Begin drawing the scene. - for render_scene_index in 0..render_scene_count { - let view = self.ui.mode.view(render_scene_index); - let viewport = self.window.viewport(view); - self.window.make_current(view); - self.renderer.set_dest_framebuffer(DestFramebuffer::Default { - viewport, - window_size: self.window_size.device_size(), - }); - self.renderer.device.clear(Some(self.background_color().to_f32().0), Some(1.0), Some(0)); - } + self.renderer.bind_dest_framebuffer(); - render_scene_count + // Clear to the appropriate color. + let clear_color = if scene_count == 2 { + ColorF::transparent_black() + } else { + self.background_color().to_f32() + }; + self.renderer.device.clear(&ClearParams { + color: Some(clear_color), + depth: Some(1.0), + stencil: Some(0), + ..ClearParams::default() + }); + + scene_count } fn build_scene(&mut self) { - let render_transforms = match self.camera { - Camera::ThreeD { ref transforms, ref mut transform, ref mut velocity, .. } => { - if transform.offset(*velocity) { + let render_transform = match self.camera { + Camera::ThreeD { + ref scene_transform, + ref mut modelview_transform, + ref mut velocity, + .. + } => { + if modelview_transform.offset(*velocity) { self.dirty = true; } - transforms.iter() - .map(|tr| { - let perspective = tr.perspective - .post_mul(&tr.view) - .post_mul(&transform.to_transform()); - RenderTransform::Perspective(perspective) - }).collect() + let perspective = scene_transform.perspective + .post_mul(&scene_transform.modelview_to_eye) + .post_mul(&modelview_transform.to_transform()); + RenderTransform::Perspective(perspective) } - Camera::TwoD(transform) => vec![RenderTransform::Transform2D(transform)], - }; - - let barrel_distortion = match self.ui.mode { - Mode::VR => Some(self.window.barrel_distortion_coefficients()), - _ => None, + Camera::TwoD(transform) => RenderTransform::Transform2D(transform), }; self.scene_thread_proxy.sender.send(MainToSceneMsg::Build(BuildOptions { - render_transforms, + render_transform, stem_darkening_font_size: if self.ui.stem_darkening_effect_enabled { Some(APPROX_FONT_SIZE * self.window_size.backing_scale_factor) } else { None }, subpixel_aa_enabled: self.ui.subpixel_aa_effect_enabled, - barrel_distortion, })).unwrap(); } @@ -277,12 +317,12 @@ impl DemoApp where W: Window { } Event::MouseMoved(new_position) if self.mouselook_enabled => { let mouse_position = self.process_mouse_position(new_position); - if let Camera::ThreeD { ref mut transform, .. } = self.camera { + if let Camera::ThreeD { ref mut modelview_transform, .. } = self.camera { let rotation = mouse_position.relative .to_f32() .scale(MOUSELOOK_ROTATION_SPEED); - transform.yaw += rotation.x(); - transform.pitch += rotation.y(); + modelview_transform.yaw += rotation.x(); + modelview_transform.pitch += rotation.y(); self.dirty = true; } } @@ -302,14 +342,14 @@ impl DemoApp where W: Window { } } Event::Look { pitch, yaw } => { - if let Camera::ThreeD { ref mut transform, .. } = self.camera { - transform.pitch += pitch; - transform.yaw += yaw; + if let Camera::ThreeD { ref mut modelview_transform, .. } = self.camera { + modelview_transform.pitch += pitch; + modelview_transform.yaw += yaw; } } - Event::CameraTransforms(new_transforms) => { - if let Camera::ThreeD { ref mut transforms, .. } = self.camera { - *transforms = new_transforms; + Event::SetEyeTransforms(new_eye_transforms) => { + if let Camera::ThreeD { ref mut eye_transforms, .. } = self.camera { + *eye_transforms = new_eye_transforms; } } Event::KeyDown(Keycode::Alphanumeric(b'w')) => { @@ -392,23 +432,101 @@ impl DemoApp where W: Window { MousePosition { absolute, relative } } - pub fn draw_scene(&mut self, render_scene_index: u32) { - let view = self.ui.mode.view(render_scene_index); - let viewport = self.window.viewport(view); + pub fn draw_scene(&mut self) { + let view = self.ui.mode.view(0); self.window.make_current(view); - self.renderer.set_dest_framebuffer(DestFramebuffer::Default { - viewport, - window_size: self.window_size.device_size(), - }); - self.draw_environment(render_scene_index); + + if self.camera.mode() != Mode::VR { + self.draw_environment(0); + } + self.render_vector_scene(); - if let Some(rendering_time) = self.renderer.shift_timer_query() { - self.current_frame.as_mut().unwrap().scene_rendering_times.push(rendering_time) + // Reattach default framebuffer. + if self.camera.mode() != Mode::VR { + return; + } + + if let DestFramebuffer::Other(scene_framebuffer) = + self.renderer.replace_dest_framebuffer(DestFramebuffer::Default { + viewport: self.window.viewport(View::Mono), + window_size: self.window_size.device_size(), + }) { + self.scene_framebuffer = Some(scene_framebuffer); } } + pub fn composite_scene(&mut self, render_scene_index: u32) { + let (eye_transforms, scene_transform, modelview_transform) = match self.camera { + Camera::ThreeD { + ref eye_transforms, + ref scene_transform, + ref modelview_transform, + .. + } if eye_transforms.len() > 1 => { + (eye_transforms, scene_transform, modelview_transform) + } + _ => return, + }; + + /* + println!("scene_transform.perspective={:?}", scene_transform.perspective); + println!("scene_transform.modelview_to_eye={:?}", scene_transform.modelview_to_eye); + println!("modelview transform={:?}", modelview_transform); + */ + + let viewport = self.window.viewport(View::Stereo(render_scene_index)); + self.renderer.replace_dest_framebuffer(DestFramebuffer::Default { + viewport, + window_size: self.window_size.device_size(), + }); + + self.renderer.bind_draw_framebuffer(); + self.renderer.device.clear(&ClearParams { + color: Some(self.background_color().to_f32()), + depth: Some(1.0), + stencil: Some(0), + rect: Some(viewport), + }); + + self.draw_environment(render_scene_index); + + let scene_framebuffer = self.scene_framebuffer.as_ref().unwrap(); + let scene_texture = self.renderer.device.framebuffer_texture(scene_framebuffer); + + let quad_scale_transform = Transform3DF32::from_scale(self.scene_view_box.size().x(), + self.scene_view_box.size().y(), + 1.0); + + let scene_transform_matrix = scene_transform.perspective + .post_mul(&scene_transform.modelview_to_eye) + .post_mul(&modelview_transform.to_transform()) + .post_mul(&quad_scale_transform); + + let eye_transform = &eye_transforms[render_scene_index as usize]; + let eye_transform_matrix = eye_transform.perspective + .post_mul(&eye_transform.modelview_to_eye) + .post_mul(&modelview_transform.to_transform()) + .post_mul(&quad_scale_transform); + + /* + println!("eye transform({}).modelview_to_eye={:?}", + render_scene_index, + eye_transform.modelview_to_eye); + println!("eye transform_matrix({})={:?}", render_scene_index, eye_transform_matrix); + println!("---"); + */ + + self.renderer.reproject_texture(scene_texture, + &scene_transform_matrix.transform, + &eye_transform_matrix.transform); + } + pub fn finish_drawing_frame(&mut self) { + if let Some(rendering_time) = self.renderer.shift_timer_query() { + self.current_frame.as_mut().unwrap().scene_rendering_times.push(rendering_time); + } + let tile_time = match self.scene_thread_proxy.receiver.recv().unwrap() { SceneToMainMsg::EndFrame { tile_time } => tile_time, _ => panic!("Expected `EndFrame`!"), @@ -435,7 +553,7 @@ impl DemoApp where W: Window { if self.options.ui != UIVisibility::None { let viewport = self.window.viewport(View::Mono); self.window.make_current(View::Mono); - self.renderer.set_dest_framebuffer(DestFramebuffer::Default { + self.renderer.replace_dest_framebuffer(DestFramebuffer::Default { viewport, window_size: self.window_size.device_size(), }); @@ -489,10 +607,11 @@ impl DemoApp where W: Window { } fn draw_environment(&self, viewport_index: u32) { - let frame = &self.current_frame.as_ref().unwrap(); - let render_transform = &frame.transforms[viewport_index as usize].clone(); + // TODO(pcwalton): Use the viewport index! - let perspective = match *render_transform { + let frame = &self.current_frame.as_ref().unwrap(); + + let perspective = match frame.transform { RenderTransform::Transform2D(..) => return, RenderTransform::Perspective(perspective) => perspective, }; @@ -542,12 +661,8 @@ impl DemoApp where W: Window { transform.post_mul(&Transform3DF32::from_scale(ground_scale, 1.0, ground_scale)); device.bind_vertex_array(&self.ground_solid_vertex_array.vertex_array); device.use_program(&self.ground_program.program); - device.set_uniform(&self.ground_program.transform_uniform, UniformData::Mat4([ - transform.c0, - transform.c1, - transform.c2, - transform.c3, - ])); + device.set_uniform(&self.ground_program.transform_uniform, + UniformData::from_transform_3d(&transform)); device.set_uniform(&self.ground_program.color_uniform, UniformData::Vec4(GROUND_SOLID_COLOR.to_f32().0)); device.draw_arrays(Primitive::TriangleFan, 4, &RenderState { @@ -716,16 +831,10 @@ impl SceneThread { } MainToSceneMsg::Build(build_options) => { self.sender.send(SceneToMainMsg::BeginFrame { - transforms: build_options.render_transforms.clone(), + transform: build_options.render_transform.clone(), }).unwrap(); let start_time = Instant::now(); - for render_transform in &build_options.render_transforms { - build_scene(&self.scene, - &build_options, - (*render_transform).clone(), - self.options.jobs, - &mut self.sender); - } + build_scene(&self.scene, &build_options, self.options.jobs, &mut self.sender); let tile_time = Instant::now() - start_time; self.sender.send(SceneToMainMsg::EndFrame { tile_time }).unwrap(); } @@ -743,14 +852,13 @@ enum MainToSceneMsg { #[derive(Clone)] struct BuildOptions { - render_transforms: Vec, + render_transform: RenderTransform, stem_darkening_font_size: Option, - barrel_distortion: Option, subpixel_aa_enabled: bool, } enum SceneToMainMsg { - BeginFrame { transforms: Vec }, + BeginFrame { transform: RenderTransform }, EndFrame { tile_time: Duration }, BeginRenderScene(SceneDescriptor), Execute(RenderCommand), @@ -772,11 +880,10 @@ impl Debug for SceneToMainMsg { fn build_scene(scene: &Scene, build_options: &BuildOptions, - render_transform: RenderTransform, jobs: Option, sink: &mut SyncSender) { let render_options = RenderOptions { - transform: render_transform.clone(), + transform: build_options.render_transform.clone(), dilation: match build_options.stem_darkening_font_size { None => Point2DF32::default(), Some(font_size) => { @@ -784,7 +891,6 @@ fn build_scene(scene: &Scene, Point2DF32::new(x, y).scale(font_size) } }, - barrel_distortion: build_options.barrel_distortion, subpixel_aa_enabled: build_options.subpixel_aa_enabled, }; @@ -954,11 +1060,15 @@ fn center_of_window(window_size: &WindowSize) -> Point2DF32 { enum Camera { TwoD(Transform2DF32), ThreeD { - // For each camera, the perspective from camera coordinates to display coordinates, + // The ocular transform used for rendering of the scene to the scene framebuffer. If we are + // performing stereoscopic rendering, this is then reprojected according to the eye + // transforms below. + scene_transform: OcularTransform, + // For each eye, the perspective from camera coordinates to display coordinates, // and the view transform from world coordinates to camera coordinates. - transforms: Vec, - // The model transform from world coordinates to SVG coordinates - transform: CameraTransform3D, + eye_transforms: Vec, + // The modelview transform from world coordinates to SVG coordinates + modelview_transform: CameraTransform3D, // The camera's velocity (in world coordinates) velocity: Point3DF32, }, @@ -982,17 +1092,37 @@ impl Camera { fn new_3d(mode: Mode, view_box: RectF32, viewport_size: Point2DI32) -> Camera { let viewport_count = mode.viewport_count(); + + let fov_y = FRAC_PI_4; let aspect = viewport_size.x() as f32 / viewport_size.y() as f32; - let projection = Transform3DF32::from_perspective(FRAC_PI_4, aspect, NEAR_CLIP_PLANE, FAR_CLIP_PLANE); - let transform = CameraTransform { - perspective: Perspective::new(&projection, viewport_size), - view: Transform3DF32::default(), + let projection = Transform3DF32::from_perspective(fov_y, + aspect, + NEAR_CLIP_PLANE, + FAR_CLIP_PLANE); + let perspective = Perspective::new(&projection, viewport_size); + + // Create a scene transform by moving the camera back from the center of the eyes so that + // its field of view encompasses the field of view of both eyes. + let z_offset = -DEFAULT_EYE_OFFSET * projection.c0.x(); + let scene_transform = OcularTransform { + perspective, + modelview_to_eye: Transform3DF32::from_translation(0.0, 0.0, z_offset), }; - let transforms = iter::repeat(transform).take(viewport_count).collect(); + + // For now, initialize the eye transforms as copies of the scene transform. + let eye_offset = DEFAULT_EYE_OFFSET; + let eye_transforms = (0..viewport_count).map(|viewport_index| { + let this_eye_offset = if viewport_index == 0 { eye_offset } else { -eye_offset }; + OcularTransform { + perspective, + modelview_to_eye: Transform3DF32::from_translation(this_eye_offset, 0.0, 0.0), + } + }).collect(); Camera::ThreeD { - transforms, - transform: CameraTransform3D::new(view_box), + scene_transform, + eye_transforms, + modelview_transform: CameraTransform3D::new(view_box), velocity: Point3DF32::default(), } } @@ -1003,14 +1133,14 @@ impl Camera { fn mode(&self) -> Mode { match *self { - Camera::ThreeD { ref transforms, .. } if 2 <= transforms.len() => Mode::VR, + Camera::ThreeD { ref eye_transforms, .. } if eye_transforms.len() >= 2 => Mode::VR, Camera::ThreeD { .. } => Mode::ThreeD, Camera::TwoD { .. } => Mode::TwoD, } } } -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] struct CameraTransform3D { position: Point3DF32, yaw: f32, @@ -1089,15 +1219,15 @@ fn emit_message(ui: &mut DemoUI, } struct Frame { - transforms: Vec, + transform: RenderTransform, ui_events: Vec, scene_rendering_times: Vec, scene_stats: Vec, } impl Frame { - fn new(transforms: Vec, ui_events: Vec) -> Frame { - Frame { transforms, ui_events, scene_rendering_times: vec![], scene_stats: vec![] } + fn new(transform: RenderTransform, ui_events: Vec) -> Frame { + Frame { transform, ui_events, scene_rendering_times: vec![], scene_stats: vec![] } } } diff --git a/demo/common/src/window.rs b/demo/common/src/window.rs index e5e167ce..c234beca 100644 --- a/demo/common/src/window.rs +++ b/demo/common/src/window.rs @@ -53,7 +53,7 @@ pub enum Event { MouseDragged(Point2DI32), Zoom(f32), Look { pitch: f32, yaw: f32 }, - CameraTransforms(Vec), + SetEyeTransforms(Vec), OpenSVG(SVGPath), User { message_type: u32, message_data: u32 }, } @@ -85,12 +85,12 @@ pub enum View { } #[derive(Clone, Copy, Debug)] -pub struct CameraTransform { +pub struct OcularTransform { // The perspective which converts from camera coordinates to display coordinates pub perspective: Perspective, // The view transform which converts from world coordinates to camera coordinates - pub view: Transform3DF32, + pub modelview_to_eye: Transform3DF32, } #[derive(Clone)] diff --git a/demo/native/Cargo.toml b/demo/native/Cargo.toml index 44ff2202..2ad16917 100644 --- a/demo/native/Cargo.toml +++ b/demo/native/Cargo.toml @@ -8,6 +8,7 @@ authors = ["Patrick Walton "] pf-no-simd = ["pathfinder_simd/pf-no-simd"] [dependencies] +color-backtrace = "0.1" gl = "0.6" jemallocator = "0.1" nfd = "0.0.4" diff --git a/demo/native/src/main.rs b/demo/native/src/main.rs index 6ec72b1b..075ab2f3 100644 --- a/demo/native/src/main.rs +++ b/demo/native/src/main.rs @@ -34,6 +34,8 @@ const DEFAULT_WINDOW_WIDTH: u32 = 1067; const DEFAULT_WINDOW_HEIGHT: u32 = 800; fn main() { + color_backtrace::install(); + let window = WindowImpl::new(); let window_size = window.size(); let options = Options::default(); @@ -49,8 +51,9 @@ fn main() { } let scene_count = app.prepare_frame(events); + app.draw_scene(); for scene_index in 0..scene_count { - app.draw_scene(scene_index); + app.composite_scene(scene_index); } app.finish_drawing_frame(); } @@ -84,17 +87,14 @@ impl Window for WindowImpl { fn viewport(&self, view: View) -> RectI32 { let (width, height) = self.window.drawable_size(); - let mut width = width as i32; - let height = height as i32; - let mut x_offset = 0; + let mut width = width as i32; + let height = height as i32; + let mut x_offset = 0; if let View::Stereo(index) = view { width = width / 2; - x_offset = width * (index as i32); + x_offset = width * (index as i32); } - RectI32::new ( - Point2DI32::new(x_offset, 0), - Point2DI32::new(width, height), - ) + RectI32::new(Point2DI32::new(x_offset, 0), Point2DI32::new(width, height)) } fn make_current(&mut self, _view: View) { diff --git a/geometry/src/color.rs b/geometry/src/color.rs index 5b3ed9bb..e25e89e3 100644 --- a/geometry/src/color.rs +++ b/geometry/src/color.rs @@ -56,6 +56,11 @@ impl Debug for ColorU { pub struct ColorF(pub F32x4); impl ColorF { + #[inline] + pub fn transparent_black() -> ColorF { + ColorF(F32x4::default()) + } + #[inline] pub fn r(&self) -> f32 { self.0[0] diff --git a/gl/src/lib.rs b/gl/src/lib.rs index 26907c9c..b065a5a7 100644 --- a/gl/src/lib.rs +++ b/gl/src/lib.rs @@ -13,9 +13,9 @@ use gl::types::{GLboolean, GLchar, GLenum, GLfloat, GLint, GLsizei, GLsizeiptr, GLuint, GLvoid}; use pathfinder_geometry::basic::point::Point2DI32; use pathfinder_geometry::basic::rect::RectI32; -use pathfinder_gpu::{BlendState, BufferData, BufferTarget, BufferUploadMode, DepthFunc, Device}; -use pathfinder_gpu::{Primitive, RenderState, ShaderKind, StencilFunc, TextureFormat}; -use pathfinder_gpu::{UniformData, VertexAttrType}; +use pathfinder_gpu::{BlendState, BufferData, BufferTarget, BufferUploadMode, ClearParams}; +use pathfinder_gpu::{DepthFunc, Device, Primitive, RenderState, ShaderKind, StencilFunc}; +use pathfinder_gpu::{TextureFormat, UniformData, VertexAttrType}; use pathfinder_simd::default::F32x4; use rustache::{HashBuilder, Render}; use std::ffi::CString; @@ -367,6 +367,14 @@ impl Device for GLDevice { UniformData::Int(value) => { gl::Uniform1i(uniform.location, value); ck(); } + UniformData::Mat2(data) => { + assert_eq!(mem::size_of::(), 4 * 4); + let data_ptr: *const F32x4 = &data; + gl::UniformMatrix2fv(uniform.location, + 1, + gl::FALSE, + data_ptr as *const GLfloat); + } UniformData::Mat4(data) => { assert_eq!(mem::size_of::<[F32x4; 4]>(), 4 * 4 * 4); let data_ptr: *const F32x4 = data.as_ptr(); @@ -487,21 +495,26 @@ impl Device for GLDevice { pixels } - // TODO(pcwalton): Switch to `ColorF`! - fn clear(&self, color: Option, depth: Option, stencil: Option) { + fn clear(&self, params: &ClearParams) { unsafe { + if let Some(rect) = params.rect { + let (origin, size) = (rect.origin(), rect.size()); + gl::Scissor(origin.x(), origin.y(), size.x(), size.y()); ck(); + gl::Enable(gl::SCISSOR_TEST); ck(); + } + let mut flags = 0; - if let Some(color) = color { + if let Some(color) = params.color { gl::ColorMask(gl::TRUE, gl::TRUE, gl::TRUE, gl::TRUE); ck(); - gl::ClearColor(color.x(), color.y(), color.z(), color.w()); ck(); + gl::ClearColor(color.r(), color.g(), color.b(), color.a()); ck(); flags |= gl::COLOR_BUFFER_BIT; } - if let Some(depth) = depth { + if let Some(depth) = params.depth { gl::DepthMask(gl::TRUE); ck(); gl::ClearDepthf(depth as _); ck(); // FIXME(pcwalton): GLES flags |= gl::DEPTH_BUFFER_BIT; } - if let Some(stencil) = stencil { + if let Some(stencil) = params.stencil { gl::StencilMask(!0); ck(); gl::ClearStencil(stencil as GLint); ck(); flags |= gl::STENCIL_BUFFER_BIT; @@ -509,6 +522,10 @@ impl Device for GLDevice { if flags != 0 { gl::Clear(flags); ck(); } + + if params.rect.is_some() { + gl::Disable(gl::SCISSOR_TEST); ck(); + } } } diff --git a/gpu/src/lib.rs b/gpu/src/lib.rs index b7886420..414de608 100644 --- a/gpu/src/lib.rs +++ b/gpu/src/lib.rs @@ -14,6 +14,8 @@ use crate::resources::ResourceLoader; use image::ImageFormat; use pathfinder_geometry::basic::point::Point2DI32; use pathfinder_geometry::basic::rect::RectI32; +use pathfinder_geometry::basic::transform3d::Transform3DF32; +use pathfinder_geometry::color::ColorF; use pathfinder_simd::default::F32x4; use rustache::HashBuilder; use std::time::Duration; @@ -75,8 +77,7 @@ pub trait Device { fn texture_size(&self, texture: &Self::Texture) -> Point2DI32; fn upload_to_texture(&self, texture: &Self::Texture, size: Point2DI32, data: &[u8]); fn read_pixels_from_default_framebuffer(&self, size: Point2DI32) -> Vec; - // TODO(pcwalton): Switch to `ColorF`! - fn clear(&self, color: Option, depth: Option, stencil: Option); + fn clear(&self, params: &ClearParams); fn draw_arrays(&self, primitive: Primitive, index_count: u32, render_state: &RenderState); fn draw_elements(&self, primitive: Primitive, index_count: u32, render_state: &RenderState); fn draw_arrays_instanced(&self, @@ -199,6 +200,7 @@ pub enum ShaderKind { #[derive(Clone, Copy)] pub enum UniformData { Int(i32), + Mat2(F32x4), Mat4([F32x4; 4]), Vec2(F32x4), Vec4(F32x4), @@ -212,6 +214,14 @@ pub enum Primitive { Lines, } +#[derive(Clone, Copy, Default)] +pub struct ClearParams { + pub color: Option, + pub rect: Option, + pub depth: Option, + pub stencil: Option, +} + #[derive(Clone, Debug)] pub struct RenderState { pub blend: BlendState, @@ -290,6 +300,13 @@ impl Default for StencilFunc { } } +impl UniformData { + #[inline] + pub fn from_transform_3d(transform: &Transform3DF32) -> UniformData { + UniformData::Mat4([transform.c0, transform.c1, transform.c2, transform.c3]) + } +} + fn load_shader_include(resources: &dyn ResourceLoader, include_name: &str) -> String { let resource = resources.slurp(&format!("shaders/{}.inc.glsl", include_name)).unwrap(); String::from_utf8_lossy(&resource).to_string() diff --git a/renderer/src/builder.rs b/renderer/src/builder.rs index 3fe90684..d19663ec 100644 --- a/renderer/src/builder.rs +++ b/renderer/src/builder.rs @@ -19,14 +19,10 @@ use pathfinder_geometry::basic::rect::RectF32; use pathfinder_geometry::basic::transform2d::Transform2DF32; use pathfinder_geometry::basic::transform3d::Perspective; use pathfinder_geometry::clip::PolygonClipper3D; -use pathfinder_geometry::distortion::BarrelDistortionCoefficients; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use std::sync::atomic::AtomicUsize; use std::u16; -// Must be a power of two. -pub const MAX_FILLS_PER_BATCH: u32 = 0x1000; - pub trait RenderCommandListener: Send + Sync { fn send(&self, command: RenderCommand); } @@ -134,7 +130,6 @@ impl<'a> SceneBuilder<'a> { pub struct RenderOptions { pub transform: RenderTransform, pub dilation: Point2DF32, - pub barrel_distortion: Option, pub subpixel_aa_enabled: bool, } @@ -143,7 +138,6 @@ impl RenderOptions { PreparedRenderOptions { transform: self.transform.prepare(bounds), dilation: self.dilation, - barrel_distortion: self.barrel_distortion, subpixel_aa_enabled: self.subpixel_aa_enabled, } } @@ -213,7 +207,6 @@ impl RenderTransform { pub struct PreparedRenderOptions { pub transform: PreparedRenderTransform, pub dilation: Point2DF32, - pub barrel_distortion: Option, pub subpixel_aa_enabled: bool, } diff --git a/renderer/src/gpu/renderer.rs b/renderer/src/gpu/renderer.rs index 3467e359..b4f7ab0b 100644 --- a/renderer/src/gpu/renderer.rs +++ b/renderer/src/gpu/renderer.rs @@ -16,14 +16,16 @@ use crate::scene::{ObjectShader, SceneDescriptor}; use crate::tiles::{TILE_HEIGHT, TILE_WIDTH}; use pathfinder_geometry::basic::point::{Point2DI32, Point3DF32}; use pathfinder_geometry::basic::rect::RectI32; +use pathfinder_geometry::basic::transform3d::Transform3DF32; use pathfinder_geometry::color::ColorF; use pathfinder_gpu::resources::ResourceLoader; -use pathfinder_gpu::{BlendState, BufferData, BufferTarget, BufferUploadMode, DepthFunc}; -use pathfinder_gpu::{DepthState, Device, Primitive, RenderState, StencilFunc, StencilState}; -use pathfinder_gpu::{TextureFormat, UniformData, VertexAttrType}; +use pathfinder_gpu::{BlendState, BufferData, BufferTarget, BufferUploadMode, ClearParams}; +use pathfinder_gpu::{DepthFunc, DepthState, Device, Primitive, RenderState, StencilFunc}; +use pathfinder_gpu::{StencilState, TextureFormat, UniformData, VertexAttrType}; use pathfinder_simd::default::{F32x4, I32x4}; use std::cmp; use std::collections::VecDeque; +use std::mem; use std::ops::{Add, Div}; use std::time::Duration; use std::u32; @@ -75,6 +77,10 @@ pub struct Renderer where D: Device { stencil_program: StencilProgram, stencil_vertex_array: StencilVertexArray, + // Reprojection shader + reprojection_program: ReprojectionProgram, + reprojection_vertex_array: ReprojectionVertexArray, + // Rendering state mask_framebuffer_cleared: bool, buffered_fills: Vec, @@ -103,6 +109,7 @@ impl Renderer where D: Device { let postprocess_program = PostprocessProgram::new(&device, resources); let stencil_program = StencilProgram::new(&device, resources); + let reprojection_program = ReprojectionProgram::new(&device, resources); let area_lut_texture = device.create_texture_from_png(resources, "area-lut"); let gamma_lut_texture = device.create_texture_from_png(resources, "gamma-lut"); @@ -136,6 +143,10 @@ impl Renderer where D: Device { &postprocess_program, &quad_vertex_positions_buffer); let stencil_vertex_array = StencilVertexArray::new(&device, &stencil_program); + let reprojection_vertex_array = + ReprojectionVertexArray::new(&device, + &reprojection_program, + &quad_vertex_positions_buffer); let mask_framebuffer_size = Point2DI32::new(MASK_FRAMEBUFFER_WIDTH, MASK_FRAMEBUFFER_HEIGHT); @@ -176,6 +187,9 @@ impl Renderer where D: Device { stencil_program, stencil_vertex_array, + reprojection_program, + reprojection_vertex_array, + stats: RenderStats::default(), current_timer_query: None, pending_timer_queries: VecDeque::new(), @@ -240,7 +254,7 @@ impl Renderer where D: Device { } pub fn draw_debug_ui(&self) { - self.bind_main_framebuffer(); + self.bind_dest_framebuffer(); self.debug_ui.draw(&self.device); } @@ -256,8 +270,14 @@ impl Renderer where D: Device { } #[inline] - pub fn set_dest_framebuffer(&mut self, new_dest_framebuffer: DestFramebuffer) { - self.dest_framebuffer = new_dest_framebuffer; + pub fn dest_framebuffer(&self) -> &DestFramebuffer { + &self.dest_framebuffer + } + + #[inline] + pub fn replace_dest_framebuffer(&mut self, new_dest_framebuffer: DestFramebuffer) + -> DestFramebuffer { + mem::replace(&mut self.dest_framebuffer, new_dest_framebuffer) } #[inline] @@ -315,7 +335,10 @@ impl Renderer where D: Device { self.device.bind_framebuffer(&self.mask_framebuffer); // TODO(pcwalton): Only clear the appropriate portion? - self.device.clear(Some(F32x4::splat(0.0)), None, None); + self.device.clear(&ClearParams { + color: Some(ColorF::transparent_black()), + ..ClearParams::default() + }); } fn add_fills(&mut self, mut fills: &[FillBatchPrimitive]) { @@ -504,7 +527,7 @@ impl Renderer where D: Device { } } - self.bind_main_framebuffer(); + self.bind_dest_framebuffer(); self.device.bind_vertex_array(&self.postprocess_vertex_array.vertex_array); self.device.use_program(&self.postprocess_program.program); @@ -596,15 +619,37 @@ impl Renderer where D: Device { }) } - fn bind_draw_framebuffer(&self) { + pub fn reproject_texture(&self, + texture: &D::Texture, + old_transform: &Transform3DF32, + new_transform: &Transform3DF32) { + self.bind_draw_framebuffer(); + + self.device.bind_vertex_array(&self.reprojection_vertex_array.vertex_array); + self.device.use_program(&self.reprojection_program.program); + self.device.set_uniform(&self.reprojection_program.old_transform_uniform, + UniformData::from_transform_3d(old_transform)); + self.device.set_uniform(&self.reprojection_program.new_transform_uniform, + UniformData::from_transform_3d(new_transform)); + self.device.bind_texture(texture, 0); + self.device.set_uniform(&self.reprojection_program.texture_uniform, + UniformData::TextureUnit(0)); + self.device.draw_arrays(Primitive::TriangleFan, 4, &RenderState { + blend: BlendState::RGBSrcAlphaAlphaOneMinusSrcAlpha, + depth: Some(DepthState { func: DepthFunc::Less, write: false }), + ..RenderState::default() + }); + } + + pub fn bind_draw_framebuffer(&self) { if self.postprocessing_needed() { self.device.bind_framebuffer(self.postprocess_source_framebuffer.as_ref().unwrap()); } else { - self.bind_main_framebuffer(); + self.bind_dest_framebuffer(); } } - fn bind_main_framebuffer(&self) { + pub fn bind_dest_framebuffer(&self) { match self.dest_framebuffer { DestFramebuffer::Default { viewport, .. } => { self.device.bind_default_framebuffer(viewport) @@ -634,7 +679,10 @@ impl Renderer where D: Device { }; self.device.bind_framebuffer(self.postprocess_source_framebuffer.as_ref().unwrap()); - self.device.clear(Some(F32x4::default()), None, None); + self.device.clear(&ClearParams { + color: Some(ColorF::transparent_black()), + ..ClearParams::default() + }); } fn postprocessing_needed(&self) -> bool { @@ -1099,6 +1147,51 @@ impl StencilVertexArray where D: Device { } } +struct ReprojectionProgram where D: Device { + program: D::Program, + old_transform_uniform: D::Uniform, + new_transform_uniform: D::Uniform, + texture_uniform: D::Uniform, +} + +impl ReprojectionProgram where D: Device { + fn new(device: &D, resources: &dyn ResourceLoader) -> ReprojectionProgram { + let program = device.create_program(resources, "reproject"); + let old_transform_uniform = device.get_uniform(&program, "OldTransform"); + let new_transform_uniform = device.get_uniform(&program, "NewTransform"); + let texture_uniform = device.get_uniform(&program, "Texture"); + + ReprojectionProgram { + program, + old_transform_uniform, + new_transform_uniform, + texture_uniform, + } + } +} + +struct ReprojectionVertexArray where D: Device { + vertex_array: D::VertexArray, +} + +impl ReprojectionVertexArray where D: Device { + fn new(device: &D, + reprojection_program: &ReprojectionProgram, + quad_vertex_positions_buffer: &D::Buffer) + -> ReprojectionVertexArray { + let vertex_array = device.create_vertex_array(); + + let position_attr = device.get_vertex_attr(&reprojection_program.program, "Position"); + + device.bind_vertex_array(&vertex_array); + device.use_program(&reprojection_program.program); + device.bind_buffer(quad_vertex_positions_buffer, BufferTarget::Vertex); + device.configure_float_vertex_attr(&position_attr, 2, VertexAttrType::U8, false, 0, 0, 0); + + ReprojectionVertexArray { vertex_array } + } +} + #[derive(Clone)] pub enum DestFramebuffer where D: Device { Default { viewport: RectI32, window_size: Point2DI32 }, diff --git a/renderer/src/scene.rs b/renderer/src/scene.rs index d2b0a8e8..bee5502f 100644 --- a/renderer/src/scene.rs +++ b/renderer/src/scene.rs @@ -83,11 +83,6 @@ impl Scene { outline.clip_against_polygon(clip_polygon); outline.apply_perspective(perspective); - // TODO(pcwalton): Support this in 2D too. - if let Some(barrel_distortion) = options.barrel_distortion { - outline.barrel_distort(barrel_distortion, perspective.window_size); - } - // TODO(pcwalton): Support subpixel AA in 3D. } } diff --git a/resources/shaders/debug_texture.fs.glsl b/resources/shaders/debug_texture.fs.glsl index 0095d1ca..2065c2a6 100644 --- a/resources/shaders/debug_texture.fs.glsl +++ b/resources/shaders/debug_texture.fs.glsl @@ -1,6 +1,6 @@ #version {{version}} -// pathfinder/demo/shaders/debug_texture.fs.glsl +// pathfinder/resources/shaders/debug_texture.fs.glsl // // Copyright © 2019 The Pathfinder Project Developers. // diff --git a/resources/shaders/post.vs.glsl b/resources/shaders/post.vs.glsl index 9d4b0adf..44b692ae 100644 --- a/resources/shaders/post.vs.glsl +++ b/resources/shaders/post.vs.glsl @@ -1,6 +1,6 @@ #version {{version}} -// pathfinder/demo/shaders/post.vs.glsl +// pathfinder/resources/shaders/post.vs.glsl // // Copyright © 2019 The Pathfinder Project Developers. // diff --git a/resources/shaders/reproject.fs.glsl b/resources/shaders/reproject.fs.glsl new file mode 100644 index 00000000..a1849a98 --- /dev/null +++ b/resources/shaders/reproject.fs.glsl @@ -0,0 +1,26 @@ +#version {{version}} + +// pathfinder/resources/shaders/reproject.fs.glsl +// +// Copyright © 2019 The Pathfinder Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +precision highp float; + +uniform mat4 uOldTransform; +uniform sampler2D uTexture; + +in vec2 vTexCoord; + +out vec4 oFragColor; + +void main() { + vec4 normTexCoord = uOldTransform * vec4(vTexCoord, 0.0, 1.0); + vec2 texCoord = ((normTexCoord.xy / normTexCoord.w) + 1.0) * 0.5; + oFragColor = texture(uTexture, texCoord); +} diff --git a/resources/shaders/reproject.vs.glsl b/resources/shaders/reproject.vs.glsl new file mode 100644 index 00000000..d297058d --- /dev/null +++ b/resources/shaders/reproject.vs.glsl @@ -0,0 +1,24 @@ +#version {{version}} + +// pathfinder/resources/shaders/reproject.vs.glsl +// +// Copyright © 2019 The Pathfinder Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +precision highp float; + +uniform mat4 uNewTransform; + +in vec2 aPosition; + +out vec2 vTexCoord; + +void main() { + vTexCoord = aPosition; + gl_Position = uNewTransform * vec4(aPosition, 0.0, 1.0); +}